summaryrefslogtreecommitdiff
path: root/drivers/media
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/Kconfig22
-rw-r--r--drivers/media/common/Kconfig2
-rw-r--r--drivers/media/common/ir-functions.c3
-rw-r--r--drivers/media/common/ir-keymaps.c146
-rw-r--r--drivers/media/common/saa7146_fops.c9
-rw-r--r--drivers/media/common/saa7146_vbi.c10
-rw-r--r--drivers/media/common/saa7146_video.c8
-rw-r--r--drivers/media/dvb/b2c2/flexcop.c1
-rw-r--r--drivers/media/dvb/bt8xx/bt878.c76
-rw-r--r--drivers/media/dvb/bt8xx/bt878.h6
-rw-r--r--drivers/media/dvb/bt8xx/dst.c19
-rw-r--r--drivers/media/dvb/bt8xx/dst_common.h5
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c6
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h43
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ringbuffer.c5
-rw-r--r--drivers/media/dvb/dvb-usb/af9005.c7
-rw-r--r--drivers/media/dvb/dvb-usb/au6610.c6
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.c436
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.h7
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_core.c5
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c149
-rw-r--r--drivers/media/dvb/dvb-usb/digitv.c3
-rw-r--r--drivers/media/dvb/dvb-usb/digitv.h3
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h18
-rw-r--r--drivers/media/dvb/dvb-usb/gl861.c6
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.c16
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.h1
-rw-r--r--drivers/media/dvb/dvb-usb/opera1.c6
-rw-r--r--drivers/media/dvb/dvb-usb/opera1.h9
-rw-r--r--drivers/media/dvb/dvb-usb/vp702x.c15
-rw-r--r--drivers/media/dvb/dvb-usb/vp702x.h2
-rw-r--r--drivers/media/dvb/dvb-usb/vp7045.c5
-rw-r--r--drivers/media/dvb/dvb-usb/vp7045.h5
-rw-r--r--drivers/media/dvb/frontends/Kconfig16
-rw-r--r--drivers/media/dvb/frontends/Makefile5
-rw-r--r--drivers/media/dvb/frontends/dib0070.c13
-rw-r--r--drivers/media/dvb/frontends/dib3000mc.c10
-rw-r--r--drivers/media/dvb/frontends/dib7000m.c9
-rw-r--r--drivers/media/dvb/frontends/dib7000p.c18
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.h5
-rw-r--r--drivers/media/dvb/frontends/mt2266.c204
-rw-r--r--drivers/media/dvb/frontends/mt312.c257
-rw-r--r--drivers/media/dvb/frontends/mt312.h15
-rw-r--r--drivers/media/dvb/frontends/mt352.c8
-rw-r--r--drivers/media/dvb/frontends/or51132.c6
-rw-r--r--drivers/media/dvb/frontends/or51211.c6
-rw-r--r--drivers/media/dvb/frontends/s5h1409.c97
-rw-r--r--drivers/media/dvb/frontends/s5h1409.h11
-rw-r--r--drivers/media/dvb/frontends/tda18271-common.c653
-rw-r--r--drivers/media/dvb/frontends/tda18271-fe.c1225
-rw-r--r--drivers/media/dvb/frontends/tda18271-priv.h212
-rw-r--r--drivers/media/dvb/frontends/tda18271-tables.c1285
-rw-r--r--drivers/media/dvb/frontends/tda18271.h78
-rw-r--r--drivers/media/dvb/frontends/tda827x.c367
-rw-r--r--drivers/media/dvb/frontends/tda827x.h7
-rw-r--r--drivers/media/dvb/frontends/ves1820.c4
-rw-r--r--drivers/media/dvb/frontends/xc5000.c964
-rw-r--r--drivers/media/dvb/frontends/xc5000.h62
-rw-r--r--drivers/media/dvb/frontends/xc5000_priv.h36
-rw-r--r--drivers/media/dvb/frontends/zl10353.c327
-rw-r--r--drivers/media/dvb/frontends/zl10353.h9
-rw-r--r--drivers/media/dvb/frontends/zl10353_priv.h23
-rw-r--r--drivers/media/dvb/ttpci/Kconfig37
-rw-r--r--drivers/media/dvb/ttpci/Makefile12
-rw-r--r--drivers/media/dvb/ttpci/av7110.c3
-rw-r--r--drivers/media/dvb/ttpci/av7110.h7
-rw-r--r--drivers/media/dvb/ttpci/av7110_av.c16
-rw-r--r--drivers/media/dvb/ttpci/av7110_av.h3
-rw-r--r--drivers/media/dvb/ttpci/av7110_v4l.c4
-rw-r--r--drivers/media/radio/Kconfig10
-rw-r--r--drivers/media/radio/Makefile1
-rw-r--r--drivers/media/radio/dsbr100.c18
-rw-r--r--drivers/media/radio/radio-gemtek.c6
-rw-r--r--drivers/media/radio/radio-maestro.c2
-rw-r--r--drivers/media/radio/radio-sf16fmi.c2
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c3
-rw-r--r--drivers/media/radio/radio-si470x.c1432
-rw-r--r--drivers/media/video/Kconfig52
-rw-r--r--drivers/media/video/Makefile10
-rw-r--r--drivers/media/video/bt8xx/Kconfig2
-rw-r--r--drivers/media/video/bt8xx/Makefile2
-rw-r--r--drivers/media/video/bt8xx/bttv-audio-hook.c382
-rw-r--r--drivers/media/video/bt8xx/bttv-audio-hook.h23
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c444
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c2374
-rw-r--r--drivers/media/video/bt8xx/bttv-input.c5
-rw-r--r--drivers/media/video/bt8xx/bttv-risc.c14
-rw-r--r--drivers/media/video/bt8xx/bttv-vbi.c58
-rw-r--r--drivers/media/video/bt8xx/bttv.h5
-rw-r--r--drivers/media/video/bt8xx/bttvp.h20
-rw-r--r--drivers/media/video/bw-qcam.c10
-rw-r--r--drivers/media/video/cs5345.c168
-rw-r--r--drivers/media/video/cs53l32a.c78
-rw-r--r--drivers/media/video/cx2341x.c313
-rw-r--r--drivers/media/video/cx23885/Kconfig4
-rw-r--r--drivers/media/video/cx23885/Makefile2
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c135
-rw-r--r--drivers/media/video/cx23885/cx23885-core.c296
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c179
-rw-r--r--drivers/media/video/cx23885/cx23885-i2c.c98
-rw-r--r--drivers/media/video/cx23885/cx23885-reg.h13
-rw-r--r--drivers/media/video/cx23885/cx23885-vbi.c258
-rw-r--r--drivers/media/video/cx23885/cx23885-video.c1557
-rw-r--r--drivers/media/video/cx23885/cx23885.h168
-rw-r--r--drivers/media/video/cx25840/cx25840-audio.c123
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c695
-rw-r--r--drivers/media/video/cx25840/cx25840-core.h1
-rw-r--r--drivers/media/video/cx25840/cx25840-firmware.c5
-rw-r--r--drivers/media/video/cx25840/cx25840-vbi.c2
-rw-r--r--drivers/media/video/cx88/Kconfig1
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c2
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c92
-rw-r--r--drivers/media/video/cx88/cx88-cards.c120
-rw-r--r--drivers/media/video/cx88/cx88-core.c4
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c33
-rw-r--r--drivers/media/video/cx88/cx88-i2c.c27
-rw-r--r--drivers/media/video/cx88/cx88-input.c6
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c39
-rw-r--r--drivers/media/video/cx88/cx88-vbi.c10
-rw-r--r--drivers/media/video/cx88/cx88-video.c58
-rw-r--r--drivers/media/video/cx88/cx88.h11
-rw-r--r--drivers/media/video/dpc7146.c19
-rw-r--r--drivers/media/video/em28xx/Kconfig17
-rw-r--r--drivers/media/video/em28xx/Makefile6
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c489
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c688
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c88
-rw-r--r--drivers/media/video/em28xx/em28xx-i2c.c81
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c52
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c2622
-rw-r--r--drivers/media/video/em28xx/em28xx.h166
-rw-r--r--drivers/media/video/et61x251/et61x251_core.c157
-rw-r--r--drivers/media/video/et61x251/et61x251_sensor.h8
-rw-r--r--drivers/media/video/ir-kbd-i2c.c3
-rw-r--r--drivers/media/video/ivtv/Kconfig2
-rw-r--r--drivers/media/video/ivtv/Makefile5
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.c173
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.h27
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c117
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h26
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.c52
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.c24
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.c119
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.h1
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c127
-rw-r--r--drivers/media/video/ivtv/ivtv-irq.c95
-rw-r--r--drivers/media/video/ivtv/ivtv-mailbox.c2
-rw-r--r--drivers/media/video/ivtv/ivtv-mailbox.h2
-rw-r--r--drivers/media/video/ivtv/ivtv-routing.c25
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c25
-rw-r--r--drivers/media/video/ivtv/ivtv-version.h2
-rw-r--r--drivers/media/video/ivtv/ivtv-yuv.c1094
-rw-r--r--drivers/media/video/ivtv/ivtv-yuv.h12
-rw-r--r--drivers/media/video/ivtv/ivtvfb.c6
-rw-r--r--drivers/media/video/ks0127.c1
-rw-r--r--drivers/media/video/m52790.c168
-rw-r--r--drivers/media/video/meye.c2
-rw-r--r--drivers/media/video/msp3400-driver.c238
-rw-r--r--drivers/media/video/msp3400-kthreads.c166
-rw-r--r--drivers/media/video/mt20xx.c2
-rw-r--r--drivers/media/video/mxb.c40
-rw-r--r--drivers/media/video/pvrusb2/Kconfig37
-rw-r--r--drivers/media/video/pvrusb2/Makefile2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-audio.c62
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.c55
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.h5
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c97
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debug.h39
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debugifc.c177
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.c217
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.h119
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-eeprom.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.c75
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h80
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c1301
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.h144
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.c10
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-main.c6
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-std.c7
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.c60
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c7
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-video-v4l.c55
-rw-r--r--drivers/media/video/saa7115.c80
-rw-r--r--drivers/media/video/saa7127.c148
-rw-r--r--drivers/media/video/saa7134/Kconfig13
-rw-r--r--drivers/media/video/saa7134/Makefile1
-rw-r--r--drivers/media/video/saa7134/saa7134-alsa.c16
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c614
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c35
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c19
-rw-r--r--drivers/media/video/saa7134/saa7134-empress.c293
-rw-r--r--drivers/media/video/saa7134/saa7134-i2c.c15
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c63
-rw-r--r--drivers/media/video/saa7134/saa7134-oss.c1046
-rw-r--r--drivers/media/video/saa7134/saa7134-ts.c8
-rw-r--r--drivers/media/video/saa7134/saa7134-tvaudio.c79
-rw-r--r--drivers/media/video/saa7134/saa7134-vbi.c8
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c1536
-rw-r--r--drivers/media/video/saa7134/saa7134.h34
-rw-r--r--drivers/media/video/sn9c102/Makefile1
-rw-r--r--drivers/media/video/sn9c102/sn9c102_core.c4
-rw-r--r--drivers/media/video/sn9c102/sn9c102_devtable.h2
-rw-r--r--drivers/media/video/sn9c102/sn9c102_mt9v111.c259
-rw-r--r--drivers/media/video/stk-sensor.c578
-rw-r--r--drivers/media/video/stk-webcam.c1465
-rw-r--r--drivers/media/video/stk-webcam.h138
-rw-r--r--drivers/media/video/tda7432.c232
-rw-r--r--drivers/media/video/tda8290.c901
-rw-r--r--drivers/media/video/tda8290.h31
-rw-r--r--drivers/media/video/tda9875.c167
-rw-r--r--drivers/media/video/tda9887.c305
-rw-r--r--drivers/media/video/tda9887.h38
-rw-r--r--drivers/media/video/tea5761.c2
-rw-r--r--drivers/media/video/tea5767.c92
-rw-r--r--drivers/media/video/tea5767.h19
-rw-r--r--drivers/media/video/tlv320aic23b.c134
-rw-r--r--drivers/media/video/tuner-core.c748
-rw-r--r--drivers/media/video/tuner-driver.h99
-rw-r--r--drivers/media/video/tuner-i2c.h57
-rw-r--r--drivers/media/video/tuner-simple.c17
-rw-r--r--drivers/media/video/tuner-types.c12
-rw-r--r--drivers/media/video/tuner-xc2028-types.h128
-rw-r--r--drivers/media/video/tuner-xc2028.c1213
-rw-r--r--drivers/media/video/tuner-xc2028.h63
-rw-r--r--drivers/media/video/tvaudio.c504
-rw-r--r--drivers/media/video/tveeprom.c504
-rw-r--r--drivers/media/video/upd64031a.c111
-rw-r--r--drivers/media/video/upd64083.c103
-rw-r--r--drivers/media/video/usbvision/usbvision-cards.c18
-rw-r--r--drivers/media/video/usbvision/usbvision-cards.h1
-rw-r--r--drivers/media/video/usbvision/usbvision-core.c330
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c60
-rw-r--r--drivers/media/video/usbvision/usbvision.h8
-rw-r--r--drivers/media/video/v4l2-common.c32
-rw-r--r--drivers/media/video/v4l2-int-device.c6
-rw-r--r--drivers/media/video/videobuf-core.c357
-rw-r--r--drivers/media/video/videobuf-dma-sg.c23
-rw-r--r--drivers/media/video/videobuf-dvb.c2
-rw-r--r--drivers/media/video/videobuf-vmalloc.c2
-rw-r--r--drivers/media/video/videodev.c8
-rw-r--r--drivers/media/video/vino.c22
-rw-r--r--drivers/media/video/vivi.c706
-rw-r--r--drivers/media/video/vp27smpx.c113
-rw-r--r--drivers/media/video/wm8739.c137
-rw-r--r--drivers/media/video/wm8775.c140
-rw-r--r--drivers/media/video/zr364xx.c2
247 files changed, 28203 insertions, 11652 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 1604f0490404..8f4a45346de7 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -69,11 +69,13 @@ source "drivers/media/common/Kconfig"
config VIDEO_TUNER
tristate
depends on I2C
+ select TUNER_XC2028 if !VIDEO_TUNER_CUSTOMIZE
select TUNER_MT20XX if !VIDEO_TUNER_CUSTOMIZE
select TUNER_TDA8290 if !VIDEO_TUNER_CUSTOMIZE
select TUNER_TEA5761 if !VIDEO_TUNER_CUSTOMIZE
select TUNER_TEA5767 if !VIDEO_TUNER_CUSTOMIZE
select TUNER_SIMPLE if !VIDEO_TUNER_CUSTOMIZE
+ select TUNER_TDA9887 if !VIDEO_TUNER_CUSTOMIZE
menuconfig VIDEO_TUNER_CUSTOMIZE
bool "Customize analog tuner modules to build"
@@ -89,6 +91,13 @@ menuconfig VIDEO_TUNER_CUSTOMIZE
if VIDEO_TUNER_CUSTOMIZE
+config TUNER_XC2028
+ tristate "XCeive xc2028/xc3028 tuners"
+ depends on I2C
+ default m if VIDEO_TUNER_CUSTOMIZE
+ help
+ Say Y here to include support for the xc2028/xc3028 tuners.
+
config TUNER_MT20XX
tristate "Microtune 2032 / 2050 tuners"
depends on I2C
@@ -97,8 +106,10 @@ config TUNER_MT20XX
Say Y here to include support for the MT2032 / MT2050 tuner.
config TUNER_TDA8290
- tristate "TDA 8290+8275(a) tuner combo"
+ tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
depends on I2C
+ select DVB_TDA827X
+ select DVB_TDA18271
default m if VIDEO_TUNER_CUSTOMIZE
help
Say Y here to include support for Philips TDA8290+8275(a) tuner.
@@ -120,10 +131,19 @@ config TUNER_TEA5767
config TUNER_SIMPLE
tristate "Simple tuner support"
depends on I2C
+ select TUNER_TDA9887
default m if VIDEO_TUNER_CUSTOMIZE
help
Say Y here to include support for various simple tuners.
+config TUNER_TDA9887
+ tristate "TDA 9885/6/7 analog IF demodulator"
+ depends on I2C
+ default m if VIDEO_TUNER_CUSTOMIZE
+ help
+ Say Y here to include support for Philips TDA9885/6/7
+ analog IF demodulator.
+
endif # VIDEO_TUNER_CUSTOMIZE
config VIDEOBUF_GEN
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig
index c5092ef1082f..06ca75911b7f 100644
--- a/drivers/media/common/Kconfig
+++ b/drivers/media/common/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_SAA7146
tristate
- depends on I2C
+ depends on I2C && PCI
config VIDEO_SAA7146_VV
tristate
diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c
index e7c3ab951a44..bb2a027b9483 100644
--- a/drivers/media/common/ir-functions.c
+++ b/drivers/media/common/ir-functions.c
@@ -258,7 +258,7 @@ int ir_decode_biphase(u32 *samples, int count, int low, int high)
* saa7134 */
/* decode raw bit pattern to RC5 code */
-u32 ir_rc5_decode(unsigned int code)
+static u32 ir_rc5_decode(unsigned int code)
{
unsigned int org_code = code;
unsigned int pair;
@@ -371,7 +371,6 @@ EXPORT_SYMBOL_GPL(ir_dump_samples);
EXPORT_SYMBOL_GPL(ir_decode_biphase);
EXPORT_SYMBOL_GPL(ir_decode_pulsedistance);
-EXPORT_SYMBOL_GPL(ir_rc5_decode);
EXPORT_SYMBOL_GPL(ir_rc5_timer_end);
EXPORT_SYMBOL_GPL(ir_rc5_timer_keyup);
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c
index 185e8a860c1a..a4a937c90534 100644
--- a/drivers/media/common/ir-keymaps.c
+++ b/drivers/media/common/ir-keymaps.c
@@ -1331,7 +1331,12 @@ IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE] = {
[ 0x35 ] = KEY_FASTFORWARD,
[ 0x36 ] = KEY_TV,
[ 0x37 ] = KEY_RADIO, /* FM */
- [ 0x38 ] = KEY_DVD
+ [ 0x38 ] = KEY_DVD,
+
+ [ 0x3e ] = KEY_F21, /* MCE +VOL, on Y04G0033 */
+ [ 0x3a ] = KEY_F22, /* MCE -VOL, on Y04G0033 */
+ [ 0x3b ] = KEY_F23, /* MCE +CH, on Y04G0033 */
+ [ 0x3f ] = KEY_F24 /* MCE -CH, on Y04G0033 */
};
EXPORT_SYMBOL_GPL(ir_codes_winfast);
@@ -1843,3 +1848,142 @@ IR_KEYTAB_TYPE ir_codes_fusionhdtv_mce[IR_KEYTAB_SIZE] = {
};
EXPORT_SYMBOL_GPL(ir_codes_fusionhdtv_mce);
+
+/* Pinnacle PCTV HD 800i mini remote */
+IR_KEYTAB_TYPE ir_codes_pinnacle_pctv_hd[IR_KEYTAB_SIZE] = {
+
+ [0x0f] = KEY_1,
+ [0x15] = KEY_2,
+ [0x10] = KEY_3,
+ [0x18] = KEY_4,
+ [0x1b] = KEY_5,
+ [0x1e] = KEY_6,
+ [0x11] = KEY_7,
+ [0x21] = KEY_8,
+ [0x12] = KEY_9,
+ [0x27] = KEY_0,
+
+ [0x24] = KEY_ZOOM,
+ [0x2a] = KEY_SUBTITLE,
+
+ [0x00] = KEY_MUTE,
+ [0x01] = KEY_ENTER, /* Pinnacle Logo */
+ [0x39] = KEY_POWER,
+
+ [0x03] = KEY_VOLUMEUP,
+ [0x09] = KEY_VOLUMEDOWN,
+ [0x06] = KEY_CHANNELUP,
+ [0x0c] = KEY_CHANNELDOWN,
+
+ [0x2d] = KEY_REWIND,
+ [0x30] = KEY_PLAYPAUSE,
+ [0x33] = KEY_FASTFORWARD,
+ [0x3c] = KEY_STOP,
+ [0x36] = KEY_RECORD,
+ [0x3f] = KEY_EPG, /* Labeled "?" */
+};
+EXPORT_SYMBOL_GPL(ir_codes_pinnacle_pctv_hd);
+
+/*
+ * Igor Kuznetsov <igk72@ya.ru>
+ * Andrey J. Melnikov <temnota@kmv.ru>
+ *
+ * Keytable is used by BeholdTV 60x series, M6 series at
+ * least, and probably other cards too.
+ * The "ascii-art picture" below (in comments, first row
+ * is the keycode in hex, and subsequent row(s) shows
+ * the button labels (several variants when appropriate)
+ * helps to descide which keycodes to assign to the buttons.
+ */
+IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = {
+
+ /* 0x1c 0x12 *
+ * TV/FM POWER *
+ * */
+ [ 0x1c ] = KEY_TUNER, /*XXX KEY_TV KEY_RADIO */
+ [ 0x12 ] = KEY_POWER,
+
+ /* 0x01 0x02 0x03 *
+ * 1 2 3 *
+ * *
+ * 0x04 0x05 0x06 *
+ * 4 5 6 *
+ * *
+ * 0x07 0x08 0x09 *
+ * 7 8 9 *
+ * */
+ [ 0x01 ] = KEY_1,
+ [ 0x02 ] = KEY_2,
+ [ 0x03 ] = KEY_3,
+ [ 0x04 ] = KEY_4,
+ [ 0x05 ] = KEY_5,
+ [ 0x06 ] = KEY_6,
+ [ 0x07 ] = KEY_7,
+ [ 0x08 ] = KEY_8,
+ [ 0x09 ] = KEY_9,
+
+ /* 0x0a 0x00 0x17 *
+ * RECALL 0 MODE *
+ * */
+ [ 0x0a ] = KEY_AGAIN,
+ [ 0x00 ] = KEY_0,
+ [ 0x17 ] = KEY_MODE,
+
+ /* 0x14 0x10 *
+ * ASPECT FULLSCREEN *
+ * */
+ [ 0x14 ] = KEY_SCREEN,
+ [ 0x10 ] = KEY_ZOOM,
+
+ /* 0x0b *
+ * Up *
+ * *
+ * 0x18 0x16 0x0c *
+ * Left Ok Right *
+ * *
+ * 0x015 *
+ * Down *
+ * */
+ [ 0x0b ] = KEY_CHANNELUP, /*XXX KEY_UP */
+ [ 0x18 ] = KEY_VOLUMEDOWN, /*XXX KEY_LEFT */
+ [ 0x16 ] = KEY_OK, /*XXX KEY_ENTER */
+ [ 0x0c ] = KEY_VOLUMEUP, /*XXX KEY_RIGHT */
+ [ 0x15 ] = KEY_CHANNELDOWN, /*XXX KEY_DOWN */
+
+ /* 0x11 0x0d *
+ * MUTE INFO *
+ * */
+ [ 0x11 ] = KEY_MUTE,
+ [ 0x0d ] = KEY_INFO,
+
+ /* 0x0f 0x1b 0x1a *
+ * RECORD PLAY/PAUSE STOP *
+ * *
+ * 0x0e 0x1f 0x1e *
+ *TELETEXT AUDIO SOURCE *
+ * RED YELLOW *
+ * */
+ [ 0x0f ] = KEY_RECORD,
+ [ 0x1b ] = KEY_PLAYPAUSE,
+ [ 0x1a ] = KEY_STOP,
+ [ 0x0e ] = KEY_TEXT,
+ [ 0x1f ] = KEY_RED, /*XXX KEY_AUDIO */
+ [ 0x1e ] = KEY_YELLOW, /*XXX KEY_SOURCE */
+
+ /* 0x1d 0x13 0x19 *
+ * SLEEP PREVIEW DVB *
+ * GREEN BLUE *
+ * */
+ [ 0x1d ] = KEY_SLEEP,
+ [ 0x13 ] = KEY_GREEN,
+ [ 0x19 ] = KEY_BLUE, /*XXX KEY_SAT */
+
+ /* 0x58 0x5c *
+ * FREEZE SNAPSHOT *
+ * */
+ [ 0x58 ] = KEY_SLOW,
+ [ 0x5c ] = KEY_SAVE,
+
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_behold);
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index 67d1b1b1b254..f0703d8bc3e8 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -61,7 +61,7 @@ void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
videobuf_waiton(&buf->vb,0,0);
videobuf_dma_unmap(q, dma);
videobuf_dma_free(dma);
- buf->vb.state = STATE_NEEDS_INIT;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
@@ -83,7 +83,7 @@ int saa7146_buffer_queue(struct saa7146_dev *dev,
buf->activate(dev,buf,NULL);
} else {
list_add_tail(&buf->vb.queue,&q->queue);
- buf->vb.state = STATE_QUEUED;
+ buf->vb.state = VIDEOBUF_QUEUED;
DEB_D(("adding buffer %p to queue. (active buffer present)\n", buf));
}
return 0;
@@ -174,7 +174,7 @@ void saa7146_buffer_timeout(unsigned long data)
spin_lock_irqsave(&dev->slock,flags);
if (q->curr) {
DEB_D(("timeout on %p\n", q->curr));
- saa7146_buffer_finish(dev,q,STATE_ERROR);
+ saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);
}
/* we don't restart the transfer here like other drivers do. when
@@ -366,7 +366,7 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
}
poll_wait(file, &buf->done, wait);
- if (buf->state == STATE_DONE || buf->state == STATE_ERROR) {
+ if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
DEB_D(("poll succeeded!\n"));
return POLLIN|POLLRDNORM;
}
@@ -538,6 +538,7 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
// fixme: -1 should be an insmod parameter *for the extension* (like "video_nr");
if (video_register_device(vfd, type, -1) < 0) {
ERR(("cannot register v4l2 device. skipping.\n"));
+ video_device_release(vfd);
return -1;
}
diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c
index 6103484e4442..c32dda973e92 100644
--- a/drivers/media/common/saa7146_vbi.c
+++ b/drivers/media/common/saa7146_vbi.c
@@ -205,7 +205,7 @@ static int buffer_activate(struct saa7146_dev *dev,
struct saa7146_buf *next)
{
struct saa7146_vv *vv = dev->vv_data;
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
DEB_VBI(("dev:%p, buf:%p, next:%p\n",dev,buf,next));
saa7146_set_vbi_capture(dev,buf,next);
@@ -238,7 +238,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,e
if (buf->vb.size != size)
saa7146_dma_free(dev,q,buf);
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
buf->vb.width = llength;
@@ -257,7 +257,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,e
if (0 != err)
return err;
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
buf->activate = buffer_activate;
return 0;
@@ -335,7 +335,7 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file)
saa7146_write(dev, MC1, MASK_20);
if (vv->vbi_q.curr) {
- saa7146_buffer_finish(dev,&vv->vbi_q,STATE_DONE);
+ saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE);
}
videobuf_queue_cancel(&fh->vbi_q);
@@ -458,7 +458,7 @@ static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
/* this must be += 2, one count for each field */
vv->vbi_fieldcount+=2;
vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount;
- saa7146_buffer_finish(dev,&vv->vbi_q,STATE_DONE);
+ saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE);
} else {
DEB_VBI(("dev:%p\n",dev));
}
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
index ae36d101006b..c31ab480d8e1 100644
--- a/drivers/media/common/saa7146_video.c
+++ b/drivers/media/common/saa7146_video.c
@@ -1235,7 +1235,7 @@ static int buffer_activate (struct saa7146_dev *dev,
{
struct saa7146_vv *vv = dev->vv_data;
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
saa7146_set_capture(dev,buf,next);
mod_timer(&vv->video_q.timeout, jiffies+BUFFER_TIMEOUT);
@@ -1281,7 +1281,7 @@ static int buffer_prepare(struct videobuf_queue *q,
saa7146_dma_free(dev,q,buf);
}
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct saa7146_format *sfmt;
buf->vb.bytesperline = fh->video_fmt.bytesperline;
@@ -1314,7 +1314,7 @@ static int buffer_prepare(struct videobuf_queue *q,
if (err)
goto oops;
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
buf->activate = buffer_activate;
return 0;
@@ -1453,7 +1453,7 @@ static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
/* only finish the buffer if we have one... */
if( NULL != q->curr ) {
- saa7146_buffer_finish(dev,q,STATE_DONE);
+ saa7146_buffer_finish(dev,q,VIDEOBUF_DONE);
}
saa7146_buffer_next(dev,q,0);
diff --git a/drivers/media/dvb/b2c2/flexcop.c b/drivers/media/dvb/b2c2/flexcop.c
index 29ec4183118e..2ddafd071c97 100644
--- a/drivers/media/dvb/b2c2/flexcop.c
+++ b/drivers/media/dvb/b2c2/flexcop.c
@@ -212,7 +212,6 @@ void flexcop_reset_block_300(struct flexcop_device *fc)
fc->write_ibi_reg(fc,ctrl_208,v208_save);
}
-EXPORT_SYMBOL(flexcop_reset_block_300);
struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len)
{
diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c
index 85e36a1d6d78..c7bbb40223f5 100644
--- a/drivers/media/dvb/bt8xx/bt878.c
+++ b/drivers/media/dvb/bt8xx/bt878.c
@@ -378,23 +378,37 @@ bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *
EXPORT_SYMBOL(bt878_device_control);
+#define BROOKTREE_878_DEVICE(vend, dev, name) \
+ { \
+ .vendor = PCI_VENDOR_ID_BROOKTREE, \
+ .device = PCI_DEVICE_ID_BROOKTREE_878, \
+ .subvendor = (vend), .subdevice = (dev), \
+ .driver_data = (unsigned long) name \
+ }
-static struct cards card_list[] __devinitdata = {
-
- { 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" },
- { 0x07611461, BTTV_BOARD_AVDVBT_761, "AverMedia AverTV DVB-T 761" },
- { 0x001c11bd, BTTV_BOARD_PINNACLESAT, "Pinnacle PCTV Sat" },
- { 0x002611bd, BTTV_BOARD_TWINHAN_DST, "Pinnacle PCTV SAT CI" },
- { 0x00011822, BTTV_BOARD_TWINHAN_DST, "Twinhan VisionPlus DVB" },
- { 0xfc00270f, BTTV_BOARD_TWINHAN_DST, "ChainTech digitop DST-1000 DVB-S" },
- { 0x07711461, BTTV_BOARD_AVDVBT_771, "AVermedia AverTV DVB-T 771" },
- { 0xdb1018ac, BTTV_BOARD_DVICO_DVBT_LITE, "DViCO FusionHDTV DVB-T Lite" },
- { 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" },
- { 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" },
- { 0x20007063, BTTV_BOARD_PC_HDTV, "pcHDTV HD-2000 TV" },
- { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini" }
+static struct pci_device_id bt878_pci_tbl[] __devinitdata = {
+ BROOKTREE_878_DEVICE(0x0071, 0x0101, "Nebula Electronics DigiTV"),
+ BROOKTREE_878_DEVICE(0x1461, 0x0761, "AverMedia AverTV DVB-T 761"),
+ BROOKTREE_878_DEVICE(0x11bd, 0x001c, "Pinnacle PCTV Sat"),
+ BROOKTREE_878_DEVICE(0x11bd, 0x0026, "Pinnacle PCTV SAT CI"),
+ BROOKTREE_878_DEVICE(0x1822, 0x0001, "Twinhan VisionPlus DVB"),
+ BROOKTREE_878_DEVICE(0x270f, 0xfc00,
+ "ChainTech digitop DST-1000 DVB-S"),
+ BROOKTREE_878_DEVICE(0x1461, 0x0771, "AVermedia AverTV DVB-T 771"),
+ BROOKTREE_878_DEVICE(0x18ac, 0xdb10, "DViCO FusionHDTV DVB-T Lite"),
+ BROOKTREE_878_DEVICE(0x18ac, 0xdb11, "Ultraview DVB-T Lite"),
+ BROOKTREE_878_DEVICE(0x18ac, 0xd500, "DViCO FusionHDTV 5 Lite"),
+ BROOKTREE_878_DEVICE(0x7063, 0x2000, "pcHDTV HD-2000 TV"),
+ BROOKTREE_878_DEVICE(0x1822, 0x0026, "DNTV Live! Mini"),
+ { }
};
+MODULE_DEVICE_TABLE(pci, bt878_pci_tbl);
+
+static const char * __devinit card_name(const struct pci_device_id *id)
+{
+ return id->driver_data ? (const char *)id->driver_data : "Unknown";
+}
/***********************/
/* PCI device handling */
@@ -403,15 +417,13 @@ static struct cards card_list[] __devinitdata = {
static int __devinit bt878_probe(struct pci_dev *dev,
const struct pci_device_id *pci_id)
{
- int result = 0, has_dvb = 0, i;
+ int result = 0;
unsigned char lat;
struct bt878 *bt;
#if defined(__powerpc__)
unsigned int cmd;
#endif
unsigned int cardid;
- unsigned short id;
- struct cards *dvb_cards;
printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n",
bt878_num);
@@ -423,25 +435,11 @@ static int __devinit bt878_probe(struct pci_dev *dev,
if (pci_enable_device(dev))
return -EIO;
- pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &id);
- cardid = id << 16;
- pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &id);
- cardid |= id;
-
- for (i = 0, dvb_cards = card_list; i < ARRAY_SIZE(card_list); i++, dvb_cards++) {
- if (cardid == dvb_cards->pci_id) {
- printk("%s: card id=[0x%x],[ %s ] has DVB functions.\n",
- __func__, cardid, dvb_cards->name);
- has_dvb = 1;
- }
- }
+ cardid = dev->subsystem_device << 16;
+ cardid |= dev->subsystem_vendor;
- if (!has_dvb) {
- printk("%s: card id=[0x%x], Unknown card.\nExiting..\n", __func__, cardid);
- result = -EINVAL;
-
- goto fail0;
- }
+ printk(KERN_INFO "%s: card id=[0x%x],[ %s ] has DVB functions.\n",
+ __func__, cardid, card_name(pci_id));
bt = &bt878[bt878_num];
bt->dev = dev;
@@ -572,14 +570,6 @@ static void __devexit bt878_remove(struct pci_dev *pci_dev)
return;
}
-static struct pci_device_id bt878_pci_tbl[] __devinitdata = {
- {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BROOKTREE_878,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {0,}
-};
-
-MODULE_DEVICE_TABLE(pci, bt878_pci_tbl);
-
static struct pci_driver bt878_pci_driver = {
.name = "bt878",
.id_table = bt878_pci_tbl,
diff --git a/drivers/media/dvb/bt8xx/bt878.h b/drivers/media/dvb/bt8xx/bt878.h
index d593bc145628..375fd2892a11 100644
--- a/drivers/media/dvb/bt8xx/bt878.h
+++ b/drivers/media/dvb/bt8xx/bt878.h
@@ -101,12 +101,6 @@
#define BTTV_BOARD_DVICO_DVBT_LITE 0x80
#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87
-struct cards {
- __u32 pci_id;
- __u16 card_id;
- char *name;
-};
-
extern int bt878_num;
struct bt878 {
diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c
index b7a17e69ca4d..307ff35bdf13 100644
--- a/drivers/media/dvb/bt8xx/dst.c
+++ b/drivers/media/dvb/bt8xx/dst.c
@@ -71,6 +71,7 @@ MODULE_PARM_DESC(dst_algo, "tuning algo: default is 0=(SW), 1=(HW)");
} \
} while(0)
+static int dst_command(struct dst_state *state, u8 *data, u8 len);
static void dst_packsize(struct dst_state *state, int psize)
{
@@ -80,7 +81,8 @@ static void dst_packsize(struct dst_state *state, int psize)
bt878_device_control(state->bt, DST_IG_TS, &bits);
}
-int dst_gpio_outb(struct dst_state *state, u32 mask, u32 enbb, u32 outhigh, int delay)
+static int dst_gpio_outb(struct dst_state *state, u32 mask, u32 enbb,
+ u32 outhigh, int delay)
{
union dst_gpio_packet enb;
union dst_gpio_packet bits;
@@ -109,9 +111,8 @@ int dst_gpio_outb(struct dst_state *state, u32 mask, u32 enbb, u32 outhigh, int
return 0;
}
-EXPORT_SYMBOL(dst_gpio_outb);
-int dst_gpio_inb(struct dst_state *state, u8 *result)
+static int dst_gpio_inb(struct dst_state *state, u8 *result)
{
union dst_gpio_packet rd_packet;
int err;
@@ -125,7 +126,6 @@ int dst_gpio_inb(struct dst_state *state, u8 *result)
return 0;
}
-EXPORT_SYMBOL(dst_gpio_inb);
int rdc_reset_state(struct dst_state *state)
{
@@ -145,7 +145,7 @@ int rdc_reset_state(struct dst_state *state)
}
EXPORT_SYMBOL(rdc_reset_state);
-int rdc_8820_reset(struct dst_state *state)
+static int rdc_8820_reset(struct dst_state *state)
{
dprintk(verbose, DST_DEBUG, 1, "Resetting DST");
if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, 0, NO_DELAY) < 0) {
@@ -160,9 +160,8 @@ int rdc_8820_reset(struct dst_state *state)
return 0;
}
-EXPORT_SYMBOL(rdc_8820_reset);
-int dst_pio_enable(struct dst_state *state)
+static int dst_pio_enable(struct dst_state *state)
{
if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_ENABLE, 0, NO_DELAY) < 0) {
dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
@@ -172,7 +171,6 @@ int dst_pio_enable(struct dst_state *state)
return 0;
}
-EXPORT_SYMBOL(dst_pio_enable);
int dst_pio_disable(struct dst_state *state)
{
@@ -611,7 +609,7 @@ static int dst_type_print(struct dst_state *state, u8 type)
return 0;
}
-struct tuner_types tuner_list[] = {
+static struct tuner_types tuner_list[] = {
{
.tuner_type = TUNER_TYPE_L64724,
.tuner_name = "L 64724",
@@ -1224,7 +1222,7 @@ static int dst_probe(struct dst_state *state)
return 0;
}
-int dst_command(struct dst_state *state, u8 *data, u8 len)
+static int dst_command(struct dst_state *state, u8 *data, u8 len)
{
u8 reply;
@@ -1287,7 +1285,6 @@ error:
return -EIO;
}
-EXPORT_SYMBOL(dst_command);
static int dst_get_signal(struct dst_state *state)
{
diff --git a/drivers/media/dvb/bt8xx/dst_common.h b/drivers/media/dvb/bt8xx/dst_common.h
index 87623d203a89..d88cf2add82b 100644
--- a/drivers/media/dvb/bt8xx/dst_common.h
+++ b/drivers/media/dvb/bt8xx/dst_common.h
@@ -165,10 +165,8 @@ struct dst_config
};
int rdc_reset_state(struct dst_state *state);
-int rdc_8820_reset(struct dst_state *state);
int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode);
-int dst_pio_enable(struct dst_state *state);
int dst_pio_disable(struct dst_state *state);
int dst_error_recovery(struct dst_state* state);
int dst_error_bailout(struct dst_state *state);
@@ -179,9 +177,6 @@ int read_dst(struct dst_state *state, u8 * ret, u8 len);
u8 dst_check_sum(u8 * buf, u32 len);
struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter);
struct dvb_device *dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter);
-int dst_gpio_outb(struct dst_state* state, u32 mask, u32 enbb, u32 outhigh, int delay);
-
-int dst_command(struct dst_state* state, u8 * data, u8 len);
#endif // DST_COMMON_H
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 445f02665577..925cfa6221ad 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -1202,6 +1202,10 @@ void dvb_frontend_detach(struct dvb_frontend* fe)
fe->ops.tuner_ops.release(fe);
symbol_put_addr(fe->ops.tuner_ops.release);
}
+ if (fe->ops.analog_ops.release) {
+ fe->ops.analog_ops.release(fe);
+ symbol_put_addr(fe->ops.analog_ops.release);
+ }
ptr = (void*)fe->ops.release;
if (ptr) {
fe->ops.release(fe);
@@ -1215,6 +1219,8 @@ void dvb_frontend_detach(struct dvb_frontend* fe)
fe->ops.release_sec(fe);
if (fe->ops.tuner_ops.release)
fe->ops.tuner_ops.release(fe);
+ if (fe->ops.analog_ops.release)
+ fe->ops.analog_ops.release(fe);
if (fe->ops.release)
fe->ops.release(fe);
}
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index a5262e852c82..aa4133f0bd19 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -84,6 +84,9 @@ struct dvb_tuner_ops {
/** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */
int (*calc_regs)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p, u8 *buf, int buf_len);
+ /** This is to allow setting tuner-specific configs */
+ int (*set_config)(struct dvb_frontend *fe, void *priv_cfg);
+
int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency);
int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
@@ -98,6 +101,28 @@ struct dvb_tuner_ops {
int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
};
+struct analog_demod_info {
+ char *name;
+};
+
+struct analog_demod_ops {
+
+ struct analog_demod_info info;
+
+ void (*set_params)(struct dvb_frontend *fe,
+ struct analog_parameters *params);
+ int (*has_signal)(struct dvb_frontend *fe);
+ int (*is_stereo)(struct dvb_frontend *fe);
+ int (*get_afc)(struct dvb_frontend *fe);
+ void (*tuner_status)(struct dvb_frontend *fe);
+ void (*standby)(struct dvb_frontend *fe);
+ void (*release)(struct dvb_frontend *fe);
+ int (*i2c_gate_ctrl)(struct dvb_frontend *fe, int enable);
+
+ /** This is to allow setting tuner-specific configuration */
+ int (*set_config)(struct dvb_frontend *fe, void *priv_cfg);
+};
+
struct dvb_frontend_ops {
struct dvb_frontend_info info;
@@ -143,6 +168,7 @@ struct dvb_frontend_ops {
int (*ts_bus_ctrl)(struct dvb_frontend* fe, int acquire);
struct dvb_tuner_ops tuner_ops;
+ struct analog_demod_ops analog_ops;
};
#define MAX_EVENT 8
@@ -159,18 +185,19 @@ struct dvb_fe_events {
struct dvb_frontend {
struct dvb_frontend_ops ops;
struct dvb_adapter *dvb;
- void* demodulator_priv;
- void* tuner_priv;
- void* frontend_priv;
- void* sec_priv;
+ void *demodulator_priv;
+ void *tuner_priv;
+ void *frontend_priv;
+ void *sec_priv;
+ void *analog_demod_priv;
};
-extern int dvb_register_frontend(struct dvb_adapter* dvb,
- struct dvb_frontend* fe);
+extern int dvb_register_frontend(struct dvb_adapter *dvb,
+ struct dvb_frontend *fe);
-extern int dvb_unregister_frontend(struct dvb_frontend* fe);
+extern int dvb_unregister_frontend(struct dvb_frontend *fe);
-extern void dvb_frontend_detach(struct dvb_frontend* fe);
+extern void dvb_frontend_detach(struct dvb_frontend *fe);
extern void dvb_frontend_reinitialise(struct dvb_frontend *fe);
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
index 9878183ba3f0..ac9d93cf83c6 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
@@ -261,11 +261,6 @@ EXPORT_SYMBOL(dvb_ringbuffer_init);
EXPORT_SYMBOL(dvb_ringbuffer_empty);
EXPORT_SYMBOL(dvb_ringbuffer_free);
EXPORT_SYMBOL(dvb_ringbuffer_avail);
-EXPORT_SYMBOL(dvb_ringbuffer_flush);
EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
EXPORT_SYMBOL(dvb_ringbuffer_read);
EXPORT_SYMBOL(dvb_ringbuffer_write);
-EXPORT_SYMBOL(dvb_ringbuffer_pkt_write);
-EXPORT_SYMBOL(dvb_ringbuffer_pkt_read);
-EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose);
-EXPORT_SYMBOL(dvb_ringbuffer_pkt_next);
diff --git a/drivers/media/dvb/dvb-usb/af9005.c b/drivers/media/dvb/dvb-usb/af9005.c
index 7db6eee50e39..e7f76f515b4f 100644
--- a/drivers/media/dvb/dvb-usb/af9005.c
+++ b/drivers/media/dvb/dvb-usb/af9005.c
@@ -1026,6 +1026,7 @@ static int af9005_usb_probe(struct usb_interface *intf,
static struct usb_device_id af9005_usb_table[] = {
{USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9005)},
{USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_USB_XE)},
+ {USB_DEVICE(USB_VID_ANSONIC, USB_PID_ANSONIC_DVBT_USB)},
{0},
};
@@ -1075,7 +1076,7 @@ static struct dvb_usb_device_properties af9005_properties = {
.rc_key_map_size = 0,
.rc_query = af9005_rc_query,
- .num_device_descs = 2,
+ .num_device_descs = 3,
.devices = {
{.name = "Afatech DVB-T USB1.1 stick",
.cold_ids = {&af9005_usb_table[0], NULL},
@@ -1085,6 +1086,10 @@ static struct dvb_usb_device_properties af9005_properties = {
.cold_ids = {&af9005_usb_table[1], NULL},
.warm_ids = {NULL},
},
+ {.name = "Ansonic DVB-T USB1.1 stick",
+ .cold_ids = {&af9005_usb_table[2], NULL},
+ .warm_ids = {NULL},
+ },
{NULL},
}
};
diff --git a/drivers/media/dvb/dvb-usb/au6610.c b/drivers/media/dvb/dvb-usb/au6610.c
index 18e0b16fb2a9..f3ff81314696 100644
--- a/drivers/media/dvb/dvb-usb/au6610.c
+++ b/drivers/media/dvb/dvb-usb/au6610.c
@@ -79,12 +79,12 @@ static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
if (num > 2)
return -EINVAL;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
for (i = 0; i < num; i++) {
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index 04e31cf7d530..c58365005ac1 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -15,7 +15,7 @@
*
* Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de)
* Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org)
- * Copyright (C) 2006 Chris Pascoe (c.pascoe@itee.uq.edu.au)
+ * Copyright (C) 2006, 2007 Chris Pascoe (c.pascoe@itee.uq.edu.au)
*
* 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
@@ -30,11 +30,16 @@
#include "mt352.h"
#include "mt352_priv.h"
#include "zl10353.h"
+#include "tuner-xc2028.h"
+#include "tuner-xc2028-types.h"
/* debug */
-int dvb_usb_cxusb_debug;
+static int dvb_usb_cxusb_debug;
module_param_named(debug, dvb_usb_cxusb_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
+#define deb_info(args...) dprintk(dvb_usb_cxusb_debug,0x01,args)
+#define deb_i2c(args...) if (d->udev->descriptor.idVendor == USB_VID_MEDION) \
+ dprintk(dvb_usb_cxusb_debug,0x01,args)
static int cxusb_ctrl_msg(struct dvb_usb_device *d,
u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
@@ -46,11 +51,9 @@ static int cxusb_ctrl_msg(struct dvb_usb_device *d,
sndbuf[0] = cmd;
memcpy(&sndbuf[1], wbuf, wlen);
if (wo)
- dvb_usb_generic_write(d, sndbuf, 1+wlen);
+ return dvb_usb_generic_write(d, sndbuf, 1+wlen);
else
- dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
-
- return 0;
+ return dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
}
/* GPIO */
@@ -72,6 +75,34 @@ static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff)
st->gpio_write_state[GPIO_TUNER] = onoff;
}
+static int cxusb_bluebird_gpio_rw(struct dvb_usb_device *d, u8 changemask,
+ u8 newval)
+{
+ u8 o[2], gpio_state;
+ int rc;
+
+ o[0] = 0xff & ~changemask; /* mask of bits to keep */
+ o[1] = newval & changemask; /* new values for bits */
+
+ rc = cxusb_ctrl_msg(d, CMD_BLUEBIRD_GPIO_RW, o, 2, &gpio_state, 1);
+ if (rc < 0 || (gpio_state & changemask) != (newval & changemask))
+ deb_info("bluebird_gpio_write failed.\n");
+
+ return rc < 0 ? rc : gpio_state;
+}
+
+static void cxusb_bluebird_gpio_pulse(struct dvb_usb_device *d, u8 pin, int low)
+{
+ cxusb_bluebird_gpio_rw(d, pin, low ? 0 : pin);
+ msleep(5);
+ cxusb_bluebird_gpio_rw(d, pin, low ? pin : 0);
+}
+
+static void cxusb_nano2_led(struct dvb_usb_device *d, int onoff)
+{
+ cxusb_bluebird_gpio_rw(d, 0x40, onoff ? 0 : 0x40);
+}
+
/* I2C */
static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
@@ -82,9 +113,6 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
- if (num > 2)
- warn("more than two i2c messages at a time is not handled yet. TODO.");
-
for (i = 0; i < num; i++) {
if (d->udev->descriptor.idVendor == USB_VID_MEDION)
@@ -97,8 +125,22 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
break;
}
- /* read request */
- if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
+ if (msg[i].flags & I2C_M_RD) {
+ /* read only */
+ u8 obuf[3], ibuf[1+msg[i].len];
+ obuf[0] = 0;
+ obuf[1] = msg[i].len;
+ obuf[2] = msg[i].addr;
+ if (cxusb_ctrl_msg(d, CMD_I2C_READ,
+ obuf, 3,
+ ibuf, 1+msg[i].len) < 0) {
+ warn("i2c read failed");
+ break;
+ }
+ memcpy(msg[i].buf, &ibuf[1], msg[i].len);
+ } else if (i+1 < num && (msg[i+1].flags & I2C_M_RD) &&
+ msg[i].addr == msg[i+1].addr) {
+ /* write to then read from same address */
u8 obuf[3+msg[i].len], ibuf[1+msg[i+1].len];
obuf[0] = msg[i].len;
obuf[1] = msg[i+1].len;
@@ -116,7 +158,8 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len);
i++;
- } else { /* write */
+ } else {
+ /* write only */
u8 obuf[2+msg[i].len], ibuf;
obuf[0] = msg[i].addr;
obuf[1] = msg[i].len;
@@ -131,7 +174,7 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
}
mutex_unlock(&d->i2c_mutex);
- return i;
+ return i == num ? num : -EREMOTEIO;
}
static u32 cxusb_i2c_func(struct i2c_adapter *adapter)
@@ -162,6 +205,17 @@ static int cxusb_bluebird_power_ctrl(struct dvb_usb_device *d, int onoff)
return 0;
}
+static int cxusb_nano2_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ int rc = 0;
+
+ rc = cxusb_power_ctrl(d, onoff);
+ if (!onoff)
+ cxusb_nano2_led(d, 0);
+
+ return rc;
+}
+
static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
u8 buf[2] = { 0x03, 0x00 };
@@ -197,6 +251,34 @@ static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
return 0;
}
+static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d, u32 *event,
+ int *state)
+{
+ struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+ u8 ircode[4];
+ int i;
+ struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD,
+ .buf = ircode, .len = 4 };
+
+ *event = 0;
+ *state = REMOTE_NO_KEY_PRESSED;
+
+ if (cxusb_i2c_xfer(&d->i2c_adap, &msg, 1) != 1)
+ return 0;
+
+ for (i = 0; i < d->props.rc_key_map_size; i++) {
+ if (keymap[i].custom == ircode[1] &&
+ keymap[i].data == ircode[2]) {
+ *event = keymap[i].event;
+ *state = REMOTE_KEY_PRESSED;
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
static struct dvb_usb_rc_key dvico_mce_rc_keys[] = {
{ 0xfe, 0x02, KEY_TV },
{ 0xfe, 0x0e, KEY_MP3 },
@@ -351,6 +433,20 @@ static struct mt352_config cxusb_mt352_config = {
.demod_init = cxusb_mt352_demod_init,
};
+static struct zl10353_config cxusb_zl10353_xc3028_config = {
+ .demod_address = 0x0f,
+ .if2 = 45600,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+};
+
+static struct mt352_config cxusb_mt352_xc3028_config = {
+ .demod_address = 0x0f,
+ .if2 = 4560,
+ .no_tuner = 1,
+ .demod_init = cxusb_mt352_demod_init,
+};
+
/* Callbacks for DVB USB */
static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
{
@@ -386,6 +482,51 @@ static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap)
return 0;
}
+static int dvico_bluebird_xc2028_callback(void *ptr, int command, int arg)
+{
+ struct dvb_usb_device *d = ptr;
+
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ deb_info("%s: XC2028_TUNER_RESET %d\n", __FUNCTION__, arg);
+ cxusb_bluebird_gpio_pulse(d, 0x01, 1);
+ break;
+ case XC2028_RESET_CLK:
+ deb_info("%s: XC2028_RESET_CLK %d\n", __FUNCTION__, arg);
+ break;
+ default:
+ deb_info("%s: unknown command %d, arg %d\n", __FUNCTION__,
+ command, arg);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &adap->dev->i2c_adap,
+ .i2c_addr = 0x61,
+ .video_dev = adap->dev,
+ .callback = dvico_bluebird_xc2028_callback,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = "xc3028-dvico-au-01.fw",
+ .max_len = 64,
+ .scode_table = ZARLINK456,
+ };
+
+ fe = dvb_attach(xc2028_attach, adap->fe, &cfg);
+ if (fe == NULL || fe->ops.tuner_ops.set_config == NULL)
+ return -EIO;
+
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+
+ return 0;
+}
+
static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
{
u8 b;
@@ -447,27 +588,120 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap)
return -EIO;
}
+static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ u8 ircode[4];
+ int i;
+ struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD,
+ .buf = ircode, .len = 4 };
+
+ if (usb_set_interface(adap->dev->udev, 0, 1) < 0)
+ err("set interface failed");
+
+ cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
+
+ /* reset the tuner and demodulator */
+ cxusb_bluebird_gpio_rw(adap->dev, 0x04, 0);
+ cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1);
+ cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1);
+
+ if ((adap->fe = dvb_attach(zl10353_attach,
+ &cxusb_zl10353_xc3028_config,
+ &adap->dev->i2c_adap)) == NULL)
+ return -EIO;
+
+ /* try to determine if there is no IR decoder on the I2C bus */
+ for (i = 0; adap->dev->props.rc_key_map != NULL && i < 5; i++) {
+ msleep(20);
+ if (cxusb_i2c_xfer(&adap->dev->i2c_adap, &msg, 1) != 1)
+ goto no_IR;
+ if (ircode[0] == 0 && ircode[1] == 0)
+ continue;
+ if (ircode[2] + ircode[3] != 0xff) {
+no_IR:
+ adap->dev->props.rc_key_map = NULL;
+ info("No IR receiver detected on this device.");
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ if (usb_set_interface(adap->dev->udev, 0, 1) < 0)
+ err("set interface failed");
+
+ cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
+
+ /* reset the tuner and demodulator */
+ cxusb_bluebird_gpio_rw(adap->dev, 0x04, 0);
+ cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1);
+ cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1);
+
+ if ((adap->fe = dvb_attach(zl10353_attach,
+ &cxusb_zl10353_xc3028_config,
+ &adap->dev->i2c_adap)) != NULL)
+ return 0;
+
+ if ((adap->fe = dvb_attach(mt352_attach,
+ &cxusb_mt352_xc3028_config,
+ &adap->dev->i2c_adap)) != NULL)
+ return 0;
+
+ return -EIO;
+}
+
+/*
+ * DViCO has shipped two devices with the same USB ID, but only one of them
+ * needs a firmware download. Check the device class details to see if they
+ * have non-default values to decide whether the device is actually cold or
+ * not, and forget a match if it turns out we selected the wrong device.
+ */
+static int bluebird_fx2_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ int wascold = *cold;
+
+ *cold = udev->descriptor.bDeviceClass == 0xff &&
+ udev->descriptor.bDeviceSubClass == 0xff &&
+ udev->descriptor.bDeviceProtocol == 0xff;
+
+ if (*cold && !wascold)
+ *desc = NULL;
+
+ return 0;
+}
+
/*
* DViCO bluebird firmware needs the "warm" product ID to be patched into the
* firmware file before download.
*/
-#define BLUEBIRD_01_ID_OFFSET 6638
+static const int dvico_firmware_id_offsets[] = { 6638, 3204 };
static int bluebird_patch_dvico_firmware_download(struct usb_device *udev,
const struct firmware *fw)
{
- if (fw->size < BLUEBIRD_01_ID_OFFSET + 4)
- return -EINVAL;
+ int pos;
+
+ for (pos = 0; pos < ARRAY_SIZE(dvico_firmware_id_offsets); pos++) {
+ int idoff = dvico_firmware_id_offsets[pos];
- if (fw->data[BLUEBIRD_01_ID_OFFSET] == (USB_VID_DVICO & 0xff) &&
- fw->data[BLUEBIRD_01_ID_OFFSET + 1] == USB_VID_DVICO >> 8) {
+ if (fw->size < idoff + 4)
+ continue;
- fw->data[BLUEBIRD_01_ID_OFFSET + 2] =
- le16_to_cpu(udev->descriptor.idProduct) + 1;
- fw->data[BLUEBIRD_01_ID_OFFSET + 3] =
- le16_to_cpu(udev->descriptor.idProduct) >> 8;
+ if (fw->data[idoff] == (USB_VID_DVICO & 0xff) &&
+ fw->data[idoff + 1] == USB_VID_DVICO >> 8) {
+ fw->data[idoff + 2] =
+ le16_to_cpu(udev->descriptor.idProduct) + 1;
+ fw->data[idoff + 3] =
+ le16_to_cpu(udev->descriptor.idProduct) >> 8;
- return usb_cypress_load_firmware(udev, fw, CYPRESS_FX2);
+ return usb_cypress_load_firmware(udev, fw, CYPRESS_FX2);
+ }
}
return -EINVAL;
@@ -479,6 +713,9 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties;
static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties;
static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties;
static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties;
+static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties;
+static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties;
+static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties;
static int cxusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -487,7 +724,10 @@ static int cxusb_probe(struct usb_interface *intf,
dvb_usb_device_init(intf,&cxusb_bluebird_lgh064f_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&cxusb_bluebird_dee1601_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&cxusb_bluebird_lgz201_properties,THIS_MODULE,NULL) == 0 ||
- dvb_usb_device_init(intf,&cxusb_bluebird_dtt7579_properties,THIS_MODULE,NULL) == 0) {
+ dvb_usb_device_init(intf,&cxusb_bluebird_dtt7579_properties,THIS_MODULE,NULL) == 0 ||
+ dvb_usb_device_init(intf,&cxusb_bluebird_dualdig4_properties,THIS_MODULE,NULL) == 0 ||
+ dvb_usb_device_init(intf,&cxusb_bluebird_nano2_properties,THIS_MODULE,NULL) == 0 ||
+ dvb_usb_device_init(intf,&cxusb_bluebird_nano2_needsfirmware_properties,THIS_MODULE,NULL) == 0) {
return 0;
}
@@ -508,6 +748,9 @@ static struct usb_device_id cxusb_table [] = {
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, cxusb_table);
@@ -766,6 +1009,151 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
}
};
+static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = CYPRESS_FX2,
+
+ .size_of_priv = sizeof(struct cxusb_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = cxusb_streaming_ctrl,
+ .frontend_attach = cxusb_dualdig4_frontend_attach,
+ .tuner_attach = cxusb_dvico_xc3028_tuner_attach,
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
+ },
+ },
+
+ .power_ctrl = cxusb_power_ctrl,
+
+ .i2c_algo = &cxusb_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .rc_interval = 100,
+ .rc_key_map = dvico_mce_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dvico_mce_rc_keys),
+ .rc_query = cxusb_bluebird2_rc_query,
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DViCO FusionHDTV DVB-T Dual Digital 4",
+ { NULL },
+ { &cxusb_table[13], NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = CYPRESS_FX2,
+ .identify_state = bluebird_fx2_identify_state,
+
+ .size_of_priv = sizeof(struct cxusb_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = cxusb_streaming_ctrl,
+ .frontend_attach = cxusb_nano2_frontend_attach,
+ .tuner_attach = cxusb_dvico_xc3028_tuner_attach,
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
+ },
+ },
+
+ .power_ctrl = cxusb_nano2_power_ctrl,
+
+ .i2c_algo = &cxusb_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .rc_interval = 100,
+ .rc_key_map = dvico_portable_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys),
+ .rc_query = cxusb_bluebird2_rc_query,
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DViCO FusionHDTV DVB-T NANO2",
+ { NULL },
+ { &cxusb_table[14], NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .firmware = "dvb-usb-bluebird-02.fw",
+ .download_firmware = bluebird_patch_dvico_firmware_download,
+ .identify_state = bluebird_fx2_identify_state,
+
+ .size_of_priv = sizeof(struct cxusb_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = cxusb_streaming_ctrl,
+ .frontend_attach = cxusb_nano2_frontend_attach,
+ .tuner_attach = cxusb_dvico_xc3028_tuner_attach,
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
+ },
+ },
+
+ .power_ctrl = cxusb_nano2_power_ctrl,
+
+ .i2c_algo = &cxusb_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .rc_interval = 100,
+ .rc_key_map = dvico_portable_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys),
+ .rc_query = cxusb_rc_query,
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DViCO FusionHDTV DVB-T NANO2 w/o firmware",
+ { &cxusb_table[14], NULL },
+ { &cxusb_table[15], NULL },
+ },
+ }
+};
+
static struct usb_driver cxusb_driver = {
.name = "dvb_usb_cxusb",
.probe = cxusb_probe,
diff --git a/drivers/media/dvb/dvb-usb/cxusb.h b/drivers/media/dvb/dvb-usb/cxusb.h
index c8ef77554b00..4768a2c35517 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.h
+++ b/drivers/media/dvb/dvb-usb/cxusb.h
@@ -4,12 +4,9 @@
#define DVB_USB_LOG_PREFIX "cxusb"
#include "dvb-usb.h"
-extern int dvb_usb_cxusb_debug;
-#define deb_info(args...) dprintk(dvb_usb_cxusb_debug,0x01,args)
-#define deb_i2c(args...) if (d->udev->descriptor.idVendor == USB_VID_MEDION) \
- dprintk(dvb_usb_cxusb_debug,0x01,args)
-
/* usb commands - some of it are guesses, don't have a reference yet */
+#define CMD_BLUEBIRD_GPIO_RW 0x05
+
#define CMD_I2C_WRITE 0x08
#define CMD_I2C_READ 0x09
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index 3ea294eb96bd..c9857d5c6982 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -243,7 +243,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
u8 b[4];
b[0] = REQUEST_ENABLE_VIDEO;
- b[1] = 0x00;
+ b[1] = (onoff << 4) | 0x00; /* this bit gives a kind of command, rather than enabling something or not */
b[2] = (0x01 << 4); /* Master mode */
b[3] = 0x00;
@@ -256,9 +256,6 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
b[2] |= st->channel_state;
- if (st->channel_state) /* if at least one channel is active */
- b[1] = (0x01 << 4) | 0x00;
-
deb_info("data for streaming: %x %x\n",b[1],b[2]);
return dib0700_ctrl_wr(adap->dev, b, 4);
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index 58452b52002c..e7093826e975 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -94,12 +94,28 @@ static int bristol_frontend_attach(struct dvb_usb_adapter *adap)
(10 + adap->id) << 1, &bristol_dib3000mc_config[adap->id])) == NULL ? -ENODEV : 0;
}
+static int eeprom_read(struct i2c_adapter *adap,u8 adrs,u8 *pval)
+{
+ struct i2c_msg msg[2] = {
+ { .addr = 0x50, .flags = 0, .buf = &adrs, .len = 1 },
+ { .addr = 0x50, .flags = I2C_M_RD, .buf = pval, .len = 1 },
+ };
+ if (i2c_transfer(adap, msg, 2) != 2) return -EREMOTEIO;
+ return 0;
+}
+
static int bristol_tuner_attach(struct dvb_usb_adapter *adap)
{
- struct dib0700_state *st = adap->dev->priv;
+ struct i2c_adapter *prim_i2c = &adap->dev->i2c_adap;
struct i2c_adapter *tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1);
- return dvb_attach(mt2060_attach,adap->fe, tun_i2c, &bristol_mt2060_config[adap->id],
- st->mt2060_if1[adap->id]) == NULL ? -ENODEV : 0;
+ 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 (!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],
+ if1) == NULL ? -ENODEV : 0;
}
/* STK7700D: Pinnacle/Terratec/Hauppauge Dual DVB-T Diversity */
@@ -230,6 +246,27 @@ static struct mt2266_config stk7700d_mt2266_config[2] = {
}
};
+static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ if (adap->id == 0) {
+ 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);
+ dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+ msleep(10);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+ msleep(10);
+ dib7000p_i2c_enumeration(&adap->dev->i2c_adap,1,18,stk7700d_dib7000p_mt2266_config);
+ }
+
+ adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,0x80+(adap->id << 1),
+ &stk7700d_dib7000p_mt2266_config[adap->id]);
+
+ return adap->fe == NULL ? -ENODEV : 0;
+}
+
static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap)
{
if (adap->id == 0) {
@@ -415,6 +452,35 @@ static struct dvb_usb_rc_key dib0700_rc_keys[] = {
{ 0x1e, 0x38, KEY_YELLOW },
{ 0x1e, 0x3b, KEY_GOTO },
{ 0x1e, 0x3d, KEY_POWER },
+
+ /* Key codes for the Leadtek Winfast DTV Dongle */
+ { 0x00, 0x42, KEY_POWER },
+ { 0x07, 0x7c, KEY_TUNER },
+ { 0x0f, 0x4e, KEY_PRINT }, /* PREVIEW */
+ { 0x08, 0x40, KEY_SCREEN }, /* full screen toggle*/
+ { 0x0f, 0x71, KEY_DOT }, /* frequency */
+ { 0x07, 0x43, KEY_0 },
+ { 0x0c, 0x41, KEY_1 },
+ { 0x04, 0x43, KEY_2 },
+ { 0x0b, 0x7f, KEY_3 },
+ { 0x0e, 0x41, KEY_4 },
+ { 0x06, 0x43, KEY_5 },
+ { 0x09, 0x7f, KEY_6 },
+ { 0x0d, 0x7e, KEY_7 },
+ { 0x05, 0x7c, KEY_8 },
+ { 0x0a, 0x40, KEY_9 },
+ { 0x0e, 0x4e, KEY_CLEAR },
+ { 0x04, 0x7c, KEY_CHANNEL }, /* show channel number */
+ { 0x0f, 0x41, KEY_LAST }, /* recall */
+ { 0x03, 0x42, KEY_MUTE },
+ { 0x06, 0x4c, KEY_RESERVED }, /* PIP button*/
+ { 0x01, 0x72, KEY_SHUFFLE }, /* SNAPSHOT */
+ { 0x0c, 0x4e, KEY_PLAYPAUSE }, /* TIMESHIFT */
+ { 0x0b, 0x70, KEY_RECORD },
+ { 0x03, 0x7d, KEY_VOLUMEUP },
+ { 0x01, 0x7d, KEY_VOLUMEDOWN },
+ { 0x02, 0x42, KEY_CHANNELUP },
+ { 0x00, 0x7d, KEY_CHANNELDOWN },
};
/* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
@@ -578,16 +644,22 @@ static struct mt2060_config stk7700p_mt2060_config = {
static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap)
{
+ struct i2c_adapter *prim_i2c = &adap->dev->i2c_adap;
struct dib0700_state *st = adap->dev->priv;
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 (!eeprom_read(prim_i2c,0x58,&a)) if1=1220+a;
+ }
if (st->is_dib7000pc)
tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
else
tun_i2c = dib7000m_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
return dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk7700p_mt2060_config,
- st->mt2060_if1[0]) == NULL ? -ENODEV : 0;
+ if1) == NULL ? -ENODEV : 0;
}
/* DIB7070 generic */
@@ -709,6 +781,8 @@ static struct dib7000p_config dib7070p_dib7000p_config = {
.agc_config_count = 1,
.agc = &dib7070_agc_config,
.bw = &dib7070_bw_config_12_mhz,
+ .tuner_is_baseband = 1,
+ .spur_protect = 1,
.gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
.gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
@@ -748,6 +822,8 @@ static struct dib7000p_config stk7070pd_dib7000p_config[2] = {
.agc_config_count = 1,
.agc = &dib7070_agc_config,
.bw = &dib7070_bw_config_12_mhz,
+ .tuner_is_baseband = 1,
+ .spur_protect = 1,
.gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
.gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
@@ -760,6 +836,8 @@ static struct dib7000p_config stk7070pd_dib7000p_config[2] = {
.agc_config_count = 1,
.agc = &dib7070_agc_config,
.bw = &dib7070_bw_config_12_mhz,
+ .tuner_is_baseband = 1,
+ .spur_protect = 1,
.gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
.gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
@@ -821,6 +899,12 @@ struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T) },
{ USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_VIDEOMATE_U500_PC) },
/* 20 */{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_EXPRESS) },
+ { USB_DEVICE(USB_VID_GIGABYTE, USB_PID_GIGABYTE_U7000) },
+ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14BR) },
+ { USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3000) },
+ { USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3100) },
+/* 25 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK_3) },
+ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_MYTV_T) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -862,7 +946,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
},
},
- .num_device_descs = 7,
+ .num_device_descs = 8,
.devices = {
{ "DiBcom STK7700P reference design",
{ &dib0700_usb_id_table[0], &dib0700_usb_id_table[1] },
@@ -891,6 +975,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ "AVerMedia AVerTV DVB-T Express",
{ &dib0700_usb_id_table[20] },
{ NULL },
+ },
+ { "Gigabyte U7000",
+ { &dib0700_usb_id_table[21], NULL },
+ { NULL },
}
},
@@ -961,7 +1049,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ "DiBcom STK7700D reference design",
{ &dib0700_usb_id_table[14], NULL },
{ NULL },
- },
+ }
},
.rc_interval = DEFAULT_RC_INTERVAL,
@@ -974,6 +1062,25 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .frontend_attach = stk7700P2_frontend_attach,
+ .tuner_attach = stk7700d_tuner_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+ },
+ },
+
+ .num_device_descs = 1,
+ .devices = {
+ { "ASUS My Cinema U3000 Mini DVBT Tuner",
+ { &dib0700_usb_id_table[23], NULL },
+ { NULL },
+ },
+ }
+ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
.frontend_attach = stk7070p_frontend_attach,
.tuner_attach = dib7070p_tuner_attach,
@@ -983,7 +1090,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
},
},
- .num_device_descs = 2,
+ .num_device_descs = 6,
.devices = {
{ "DiBcom STK7070P reference design",
{ &dib0700_usb_id_table[15], NULL },
@@ -993,7 +1100,29 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ &dib0700_usb_id_table[16], NULL },
{ NULL },
},
- }
+ { "Artec T14BR DVB-T",
+ { &dib0700_usb_id_table[22], NULL },
+ { NULL },
+ },
+ { "ASUS My Cinema U3100 Mini DVBT Tuner",
+ { &dib0700_usb_id_table[24], NULL },
+ { NULL },
+ },
+ { "Hauppauge Nova-T Stick",
+ { &dib0700_usb_id_table[25], NULL },
+ { NULL },
+ },
+ { "Hauppauge Nova-T MyTV.t",
+ { &dib0700_usb_id_table[26], NULL },
+ { NULL },
+ },
+ },
+
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_key_map = dib0700_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
+ .rc_query = dib0700_rc_query
+
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 2,
@@ -1024,7 +1153,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ "Pinnacle PCTV Dual DVB-T Diversity Stick",
{ &dib0700_usb_id_table[18], NULL },
{ NULL },
- },
+ }
}
},
};
diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c
index bca1e0905739..3acbda4aa27e 100644
--- a/drivers/media/dvb/dvb-usb/digitv.c
+++ b/drivers/media/dvb/dvb-usb/digitv.c
@@ -17,9 +17,10 @@
#include "nxt6000.h"
/* debug */
-int dvb_usb_digitv_debug;
+static int dvb_usb_digitv_debug;
module_param_named(debug,dvb_usb_digitv_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
+#define deb_rc(args...) dprintk(dvb_usb_digitv_debug,0x01,args)
static int digitv_ctrl_msg(struct dvb_usb_device *d,
u8 cmd, u8 vv, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
diff --git a/drivers/media/dvb/dvb-usb/digitv.h b/drivers/media/dvb/dvb-usb/digitv.h
index 8b43e3db8691..908c09f4966b 100644
--- a/drivers/media/dvb/dvb-usb/digitv.h
+++ b/drivers/media/dvb/dvb-usb/digitv.h
@@ -8,9 +8,6 @@ struct digitv_state {
int is_nxt6000;
};
-extern int dvb_usb_digitv_debug;
-#define deb_rc(args...) dprintk(dvb_usb_digitv_debug,0x01,args)
-
/* protocol (from usblogging and the SDK:
*
* Always 7 bytes bulk message(s) for controlling
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 4fa3e895028a..aa4844ef875e 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -15,7 +15,9 @@
#define USB_VID_ALCOR_MICRO 0x058f
#define USB_VID_ALINK 0x05e3
#define USB_VID_ANCHOR 0x0547
+#define USB_VID_ANSONIC 0x10b9
#define USB_VID_ANUBIS_ELECTRONIC 0x10fd
+#define USB_VID_ASUS 0x0b05
#define USB_VID_AVERMEDIA 0x07ca
#define USB_VID_COMPRO 0x185b
#define USB_VID_COMPRO_UNK 0x145f
@@ -44,12 +46,16 @@
#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
#define USB_VID_UNIWILL 0x1584
#define USB_VID_WIDEVIEW 0x14aa
+/* dom : pour gigabyte u7000 */
+#define USB_VID_GIGABYTE 0x1044
+
/* Product IDs */
#define USB_PID_ADSTECH_USB2_COLD 0xa333
#define USB_PID_ADSTECH_USB2_WARM 0xa334
#define USB_PID_AFATECH_AF9005 0x9020
#define USB_VID_ALINK_DTU 0xf170
+#define USB_PID_ANSONIC_DVBT_USB 0x6000
#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
@@ -69,6 +75,7 @@
#define USB_PID_DIBCOM_STK7700P 0x1e14
#define USB_PID_DIBCOM_STK7700P_PC 0x1e78
#define USB_PID_DIBCOM_STK7700D 0x1ef0
+#define USB_PID_DIBCOM_STK7700_U7000 0x7001
#define USB_PID_DIBCOM_STK7070P 0x1ebc
#define USB_PID_DIBCOM_STK7070PD 0x1ebe
#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131
@@ -99,6 +106,7 @@
#define USB_PID_ULTIMA_TVBOX_USB2_WARM 0x810a
#define USB_PID_ARTEC_T14_COLD 0x810b
#define USB_PID_ARTEC_T14_WARM 0x810c
+#define USB_PID_ARTEC_T14BR 0x810f
#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613
#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002
#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e
@@ -120,6 +128,8 @@
#define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950
#define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050
#define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060
+#define USB_PID_HAUPPAUGE_NOVA_T_STICK_3 0x7070
+#define USB_PID_HAUPPAUGE_MYTV_T 0x7080
#define USB_PID_HAUPPAUGE_NOVA_TD_STICK 0x9580
#define USB_PID_AVERMEDIA_EXPRESS 0xb568
#define USB_PID_AVERMEDIA_VOLAR 0xa807
@@ -143,6 +153,9 @@
#define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM 0xdb51
#define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58
#define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59
+#define USB_PID_DVICO_BLUEBIRD_DUAL_4 0xdb78
+#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2 0xdb70
+#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM 0xdb71
#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54
#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55
#define USB_PID_MEDION_MD95700 0x0932
@@ -170,6 +183,9 @@
#define USB_PID_OPERA1_WARM 0x3829
#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD 0x0514
#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM 0x0513
-
+/* dom pour gigabyte u7000 */
+#define USB_PID_GIGABYTE_U7000 0x7001
+#define USB_PID_ASUS_U3000 0x171f
+#define USB_PID_ASUS_U3100 0x173f
#endif
diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c
index f01d99c1c43c..6b99d9f4d5b3 100644
--- a/drivers/media/dvb/dvb-usb/gl861.c
+++ b/drivers/media/dvb/dvb-usb/gl861.c
@@ -56,12 +56,12 @@ static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
if (num > 2)
return -EINVAL;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
for (i = 0; i < num; i++) {
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c
index 92147ee3e14f..83e8535014c6 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk.c
@@ -171,22 +171,6 @@ static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff)
return 0;
}
-int gp8psk_bcm4500_reload(struct dvb_usb_device *d)
-{
- u8 buf;
- int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct);
- /* Turn off 8psk power */
- if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1))
- return -EINVAL;
- /* Turn On 8psk power */
- if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1))
- return -EINVAL;
- /* load BCM4500 firmware */
- if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
- if (gp8psk_load_bcm4500fw(d))
- return EINVAL;
- return 0;
-}
static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.h b/drivers/media/dvb/dvb-usb/gp8psk.h
index e83a57506cfa..e5cd8149c23d 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.h
+++ b/drivers/media/dvb/dvb-usb/gp8psk.h
@@ -92,6 +92,5 @@ extern struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d);
extern int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
extern int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen);
-extern int gp8psk_bcm4500_reload(struct dvb_usb_device *d);
#endif
diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c
index d7c04951ceab..21935bf7059e 100644
--- a/drivers/media/dvb/dvb-usb/opera1.c
+++ b/drivers/media/dvb/dvb-usb/opera1.c
@@ -10,7 +10,9 @@
* see Documentation/dvb/README.dvb-usb for more information
*/
-#include "opera1.h"
+#define DVB_USB_LOG_PREFIX "opera"
+
+#include "dvb-usb.h"
#include "stv0299.h"
#define OPERA_READ_MSG 0
@@ -38,7 +40,7 @@ struct opera_rc_keys {
u32 event;
};
-int dvb_usb_opera1_debug;
+static int dvb_usb_opera1_debug;
module_param_named(debug, dvb_usb_opera1_debug, int, 0644);
MODULE_PARM_DESC(debug,
"set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))."
diff --git a/drivers/media/dvb/dvb-usb/opera1.h b/drivers/media/dvb/dvb-usb/opera1.h
deleted file mode 100644
index 53174427902d..000000000000
--- a/drivers/media/dvb/dvb-usb/opera1.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _OPERA1_H_
-#define _OPERA1_H_
-
-#define DVB_USB_LOG_PREFIX "opera"
-#include "dvb-usb.h"
-
-extern int dvb_usb_opera1_debug;
-#define deb_xfer(args...) dprintk(dvb_usb_opera1_debug,0x02,args)
-#endif
diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c
index 16533b31a82d..e553c139ac44 100644
--- a/drivers/media/dvb/dvb-usb/vp702x.c
+++ b/drivers/media/dvb/dvb-usb/vp702x.c
@@ -56,7 +56,7 @@ int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8
return ret;
}
-int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
+static int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen)
{
int ret;
@@ -204,19 +204,6 @@ static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
return 0;
}
-int vp702x_power_ctrl(struct dvb_usb_device *d, int onoff)
-{
- struct vp702x_device_state *st = d->priv;
-
- if (st->power_state == 0 && onoff)
- vp702x_usb_out_op(d, SET_TUNER_POWER_REQ, 1, 7, NULL, 0);
- else if (st->power_state == 1 && onoff == 0)
- vp702x_usb_out_op(d, SET_TUNER_POWER_REQ, 0, 7, NULL, 0);
-
- st->power_state = onoff;
-
- return 0;
-}
static int vp702x_read_mac_addr(struct dvb_usb_device *d,u8 mac[6])
{
diff --git a/drivers/media/dvb/dvb-usb/vp702x.h b/drivers/media/dvb/dvb-usb/vp702x.h
index 25a9dee4c824..c2f97f96c21f 100644
--- a/drivers/media/dvb/dvb-usb/vp702x.h
+++ b/drivers/media/dvb/dvb-usb/vp702x.h
@@ -102,7 +102,5 @@ extern struct dvb_frontend * vp702x_fe_attach(struct dvb_usb_device *d);
extern int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec);
extern int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
-extern int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
-extern int vp702x_power_ctrl(struct dvb_usb_device *d, int onoff);
#endif
diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c
index 5bbd2d5192f0..c172babf59bb 100644
--- a/drivers/media/dvb/dvb-usb/vp7045.c
+++ b/drivers/media/dvb/dvb-usb/vp7045.c
@@ -15,9 +15,12 @@
#include "vp7045.h"
/* debug */
-int dvb_usb_vp7045_debug;
+static int dvb_usb_vp7045_debug;
module_param_named(debug,dvb_usb_vp7045_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS);
+#define deb_info(args...) dprintk(dvb_usb_vp7045_debug,0x01,args)
+#define deb_xfer(args...) dprintk(dvb_usb_vp7045_debug,0x02,args)
+#define deb_rc(args...) dprintk(dvb_usb_vp7045_debug,0x04,args)
int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen, int msec)
{
diff --git a/drivers/media/dvb/dvb-usb/vp7045.h b/drivers/media/dvb/dvb-usb/vp7045.h
index 9ce21a20fa86..969688f85267 100644
--- a/drivers/media/dvb/dvb-usb/vp7045.h
+++ b/drivers/media/dvb/dvb-usb/vp7045.h
@@ -17,11 +17,6 @@
#define DVB_USB_LOG_PREFIX "vp7045"
#include "dvb-usb.h"
-extern int dvb_usb_vp7045_debug;
-#define deb_info(args...) dprintk(dvb_usb_vp7045_debug,0x01,args)
-#define deb_xfer(args...) dprintk(dvb_usb_vp7045_debug,0x02,args)
-#define deb_rc(args...) dprintk(dvb_usb_vp7045_debug,0x04,args)
-
/* vp7045 commands */
/* Twinhan Vendor requests */
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 59b9ed1f1aec..9ad86ce4a4e5 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -316,6 +316,13 @@ config DVB_TDA827X
help
A DVB-T silicon tuner module. Say Y when you want to support this tuner.
+config DVB_TDA18271
+ tristate "NXP TDA18271 silicon tuner"
+ depends on I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A silicon tuner module. Say Y when you want to support this tuner.
+
config DVB_TUNER_QT1010
tristate "Quantek QT1010 silicon tuner"
depends on DVB_CORE && I2C
@@ -353,6 +360,15 @@ config DVB_TUNER_DIB0070
This device is only used inside a SiP called togther with a
demodulator for now.
+config DVB_TUNER_XC5000
+ tristate "Xceive XC5000 silicon tuner"
+ depends on I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A driver for the silicon tuner XC5000 from Xceive.
+ This device is only used inside a SiP called togther with a
+ demodulator for now.
+
comment "Miscellaneous devices"
depends on DVB_CORE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 4b8ad1f132aa..16bd107ebd32 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -3,6 +3,9 @@
#
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+EXTRA_CFLAGS += -Idrivers/media/video/
+
+tda18271-objs := tda18271-tables.o tda18271-common.o tda18271-fe.o
obj-$(CONFIG_DVB_PLL) += dvb-pll.o
obj-$(CONFIG_DVB_STV0299) += stv0299.o
@@ -39,6 +42,7 @@ obj-$(CONFIG_DVB_ISL6421) += isl6421.o
obj-$(CONFIG_DVB_TDA10086) += tda10086.o
obj-$(CONFIG_DVB_TDA826X) += tda826x.o
obj-$(CONFIG_DVB_TDA827X) += tda827x.o
+obj-$(CONFIG_DVB_TDA18271) += tda18271.o
obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
obj-$(CONFIG_DVB_TUNER_MT2266) += mt2266.o
obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o
@@ -46,3 +50,4 @@ obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o
obj-$(CONFIG_DVB_TUA6100) += tua6100.o
obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131.o
obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
+obj-$(CONFIG_DVB_TUNER_XC5000) += xc5000.o
diff --git a/drivers/media/dvb/frontends/dib0070.c b/drivers/media/dvb/frontends/dib0070.c
index 481eaa684157..fe895bf7b18f 100644
--- a/drivers/media/dvb/frontends/dib0070.c
+++ b/drivers/media/dvb/frontends/dib0070.c
@@ -434,9 +434,14 @@ static u16 dib0070_p1f_defaults[] =
0,
};
-static void dib0070_wbd_calibration(struct dib0070_state *state)
+static void dib0070_wbd_calibration(struct dvb_frontend *fe)
{
u16 wbd_offs;
+ struct dib0070_state *state = fe->tuner_priv;
+
+ if (state->cfg->sleep)
+ state->cfg->sleep(fe, 0);
+
dib0070_write_reg(state, 0x0f, 0x6d81);
dib0070_write_reg(state, 0x20, 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001);
msleep(9);
@@ -444,6 +449,10 @@ static void dib0070_wbd_calibration(struct dib0070_state *state)
dib0070_write_reg(state, 0x20, 0);
state->wbd_ff_offset = ((wbd_offs * 8 * 18 / 33 + 1) / 2);
dprintk( "WBDStart = %d (Vargen) - FF = %hd", (u32) wbd_offs * 1800/1024, state->wbd_ff_offset);
+
+ if (state->cfg->sleep)
+ state->cfg->sleep(fe, 1);
+
}
u16 dib0070_wbd_offset(struct dvb_frontend *fe)
@@ -560,7 +569,7 @@ struct dvb_frontend * dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter
if (dib0070_reset(state) != 0)
goto free_mem;
- dib0070_wbd_calibration(state);
+ dib0070_wbd_calibration(fe);
printk(KERN_INFO "DiB0070: successfully identified\n");
memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops));
diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c
index edae0be063f5..fa851601e7d4 100644
--- a/drivers/media/dvb/frontends/dib3000mc.c
+++ b/drivers/media/dvb/frontends/dib3000mc.c
@@ -684,6 +684,9 @@ static int dib3000mc_set_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct dib3000mc_state *state = fe->demodulator_priv;
+ int ret;
+
+ dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
state->current_bandwidth = fep->u.ofdm.bandwidth;
dib3000mc_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->u.ofdm.bandwidth));
@@ -700,7 +703,7 @@ static int dib3000mc_set_frontend(struct dvb_frontend* fe,
fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ||
fep->u.ofdm.constellation == QAM_AUTO ||
fep->u.ofdm.code_rate_HP == FEC_AUTO) {
- int i = 100, found;
+ int i = 1000, found;
dib3000mc_autosearch_start(fe, fep);
do {
@@ -715,10 +718,11 @@ static int dib3000mc_set_frontend(struct dvb_frontend* fe,
dib3000mc_get_frontend(fe, fep);
}
+ ret = dib3000mc_tune(fe, fep);
+
/* make this a config parameter */
dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO);
-
- return dib3000mc_tune(fe, fep);
+ return ret;
}
static int dib3000mc_read_status(struct dvb_frontend *fe, fe_status_t *stat)
diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c
index fb18441a8c57..5f1375e30dfc 100644
--- a/drivers/media/dvb/frontends/dib7000m.c
+++ b/drivers/media/dvb/frontends/dib7000m.c
@@ -1171,7 +1171,9 @@ static int dib7000m_set_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct dib7000m_state *state = fe->demodulator_priv;
- int time;
+ int time, ret;
+
+ dib7000m_set_output_mode(state, OUTMODE_HIGH_Z);
state->current_bandwidth = fep->u.ofdm.bandwidth;
dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->u.ofdm.bandwidth));
@@ -1206,10 +1208,11 @@ static int dib7000m_set_frontend(struct dvb_frontend* fe,
dib7000m_get_frontend(fe, fep);
}
+ ret = dib7000m_tune(fe, fep);
+
/* make this a config parameter */
dib7000m_set_output_mode(state, OUTMODE_MPEG2_FIFO);
-
- return dib7000m_tune(fe, fep);
+ return ret;
}
static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat)
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c
index f45bcfc51cf8..47c23e29753e 100644
--- a/drivers/media/dvb/frontends/dib7000p.c
+++ b/drivers/media/dvb/frontends/dib7000p.c
@@ -35,8 +35,8 @@ struct dib7000p_state {
u16 wbd_ref;
- u8 current_band;
- fe_bandwidth_t current_bandwidth;
+ u8 current_band;
+ u32 current_bandwidth;
struct dibx000_agc_config *current_agc;
u32 timf;
@@ -1074,7 +1074,7 @@ static int dib7000p_get_frontend(struct dvb_frontend* fe,
fep->inversion = INVERSION_AUTO;
- fep->u.ofdm.bandwidth = state->current_bandwidth;
+ fep->u.ofdm.bandwidth = BANDWIDTH_TO_INDEX(state->current_bandwidth);
switch ((tps >> 8) & 0x3) {
case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
@@ -1128,12 +1128,11 @@ static int dib7000p_set_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct dib7000p_state *state = fe->demodulator_priv;
- int time;
+ int time, ret;
- state->current_bandwidth = fep->u.ofdm.bandwidth;
- dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->u.ofdm.bandwidth));
+ dib7000p_set_output_mode(state, OUTMODE_HIGH_Z);
- /* maybe the parameter has been changed */
+ /* maybe the parameter has been changed */
state->sfn_workaround_active = buggy_sfn_workaround;
if (fe->ops.tuner_ops.set_params)
@@ -1166,10 +1165,11 @@ static int dib7000p_set_frontend(struct dvb_frontend* fe,
dib7000p_get_frontend(fe, fep);
}
+ ret = dib7000p_tune(fe, fep);
+
/* make this a config parameter */
dib7000p_set_output_mode(state, OUTMODE_MPEG2_FIFO);
-
- return dib7000p_tune(fe, fep);
+ return ret;
}
static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat)
diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h
index 5e17275afd25..84e4d5362922 100644
--- a/drivers/media/dvb/frontends/dibx000_common.h
+++ b/drivers/media/dvb/frontends/dibx000_common.h
@@ -128,6 +128,11 @@ enum dibx000_adc_states {
(v) == BANDWIDTH_7_MHZ ? 7000 : \
(v) == BANDWIDTH_6_MHZ ? 6000 : 8000 )
+#define BANDWIDTH_TO_INDEX(v) ( \
+ (v) == 8000 ? BANDWIDTH_8_MHZ : \
+ (v) == 7000 ? BANDWIDTH_7_MHZ : \
+ (v) == 6000 ? BANDWIDTH_6_MHZ : BANDWIDTH_8_MHZ )
+
/* Chip output mode. */
#define OUTMODE_HIGH_Z 0
#define OUTMODE_MPEG2_PAR_GATED_CLK 1
diff --git a/drivers/media/dvb/frontends/mt2266.c b/drivers/media/dvb/frontends/mt2266.c
index 03fe8265745f..54b18f94b14b 100644
--- a/drivers/media/dvb/frontends/mt2266.c
+++ b/drivers/media/dvb/frontends/mt2266.c
@@ -38,8 +38,12 @@ struct mt2266_priv {
u32 frequency;
u32 bandwidth;
+ u8 band;
};
+#define MT2266_VHF 1
+#define MT2266_UHF 0
+
/* Here, frequencies are expressed in kiloHertz to avoid 32 bits overflows */
static int debug;
@@ -90,26 +94,30 @@ static int mt2266_writeregs(struct mt2266_priv *priv,u8 *buf, u8 len)
}
// Initialisation sequences
-static u8 mt2266_init1[] = {
- REG_TUNE,
- 0x00, 0x00, 0x28, 0x00, 0x52, 0x99, 0x3f };
+static u8 mt2266_init1[] = { REG_TUNE, 0x00, 0x00, 0x28,
+ 0x00, 0x52, 0x99, 0x3f };
static u8 mt2266_init2[] = {
- 0x17, 0x6d, 0x71, 0x61, 0xc0, 0xbf, 0xff, 0xdc, 0x00, 0x0a,
- 0xd4, 0x03, 0x64, 0x64, 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x7f, 0x5e, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x77, 0x0f, 0x2d };
+ 0x17, 0x6d, 0x71, 0x61, 0xc0, 0xbf, 0xff, 0xdc, 0x00, 0x0a, 0xd4,
+ 0x03, 0x64, 0x64, 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7f, 0x5e, 0x3f, 0xff, 0xff,
+ 0xff, 0x00, 0x77, 0x0f, 0x2d
+};
+
+static u8 mt2266_init_8mhz[] = { REG_BANDWIDTH, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22 };
-static u8 mt2266_init_8mhz[] = {
- REG_BANDWIDTH,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 };
+static u8 mt2266_init_7mhz[] = { REG_BANDWIDTH, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32 };
-static u8 mt2266_init_7mhz[] = {
- REG_BANDWIDTH,
- 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32 };
+static u8 mt2266_init_6mhz[] = { REG_BANDWIDTH, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7 };
-static u8 mt2266_init_6mhz[] = {
- REG_BANDWIDTH,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7 };
+static u8 mt2266_uhf[] = { 0x1d, 0xdc, 0x00, 0x0a, 0xd4, 0x03, 0x64, 0x64,
+ 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14 };
+
+static u8 mt2266_vhf[] = { 0x1d, 0xfe, 0x00, 0x00, 0xb4, 0x03, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0x82, 0xaa, 0xf1, 0x17, 0x80, 0x1f };
#define FREF 30000 // Quartz oscillator 30 MHz
@@ -122,35 +130,78 @@ static int mt2266_set_params(struct dvb_frontend *fe, struct dvb_frontend_parame
u8 lnaband;
u8 b[10];
int i;
+ u8 band;
priv = fe->tuner_priv;
- mt2266_writereg(priv,0x17,0x6d);
- mt2266_writereg(priv,0x1c,0xff);
-
freq = params->frequency / 1000; // Hz -> kHz
+ if (freq < 470000 && freq > 230000)
+ return -EINVAL; /* Gap between VHF and UHF bands */
priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
priv->frequency = freq * 1000;
- tune=2 * freq * (8192/16) / (FREF/16);
-
- if (freq <= 495000) lnaband = 0xEE; else
- if (freq <= 525000) lnaband = 0xDD; else
- if (freq <= 550000) lnaband = 0xCC; else
- if (freq <= 580000) lnaband = 0xBB; else
- if (freq <= 605000) lnaband = 0xAA; else
- if (freq <= 630000) lnaband = 0x99; else
- if (freq <= 655000) lnaband = 0x88; else
- if (freq <= 685000) lnaband = 0x77; else
- if (freq <= 710000) lnaband = 0x66; else
- if (freq <= 735000) lnaband = 0x55; else
- if (freq <= 765000) lnaband = 0x44; else
- if (freq <= 802000) lnaband = 0x33; else
- if (freq <= 840000) lnaband = 0x22; else lnaband = 0x11;
-
- msleep(100);
- mt2266_writeregs(priv,(params->u.ofdm.bandwidth==BANDWIDTH_6_MHZ)?mt2266_init_6mhz:
- (params->u.ofdm.bandwidth==BANDWIDTH_7_MHZ)?mt2266_init_7mhz:
- mt2266_init_8mhz,sizeof(mt2266_init_8mhz));
+
+ tune = 2 * freq * (8192/16) / (FREF/16);
+ band = (freq < 300000) ? MT2266_VHF : MT2266_UHF;
+ if (band == MT2266_VHF)
+ tune *= 2;
+
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ mt2266_writeregs(priv, mt2266_init_6mhz,
+ sizeof(mt2266_init_6mhz));
+ break;
+ case BANDWIDTH_7_MHZ:
+ mt2266_writeregs(priv, mt2266_init_7mhz,
+ sizeof(mt2266_init_7mhz));
+ break;
+ case BANDWIDTH_8_MHZ:
+ default:
+ mt2266_writeregs(priv, mt2266_init_8mhz,
+ sizeof(mt2266_init_8mhz));
+ break;
+ }
+
+ if (band == MT2266_VHF && priv->band == MT2266_UHF) {
+ dprintk("Switch from UHF to VHF");
+ mt2266_writereg(priv, 0x05, 0x04);
+ mt2266_writereg(priv, 0x19, 0x61);
+ mt2266_writeregs(priv, mt2266_vhf, sizeof(mt2266_vhf));
+ } else if (band == MT2266_UHF && priv->band == MT2266_VHF) {
+ dprintk("Switch from VHF to UHF");
+ mt2266_writereg(priv, 0x05, 0x52);
+ mt2266_writereg(priv, 0x19, 0x61);
+ mt2266_writeregs(priv, mt2266_uhf, sizeof(mt2266_uhf));
+ }
+ msleep(10);
+
+ if (freq <= 495000)
+ lnaband = 0xEE;
+ else if (freq <= 525000)
+ lnaband = 0xDD;
+ else if (freq <= 550000)
+ lnaband = 0xCC;
+ else if (freq <= 580000)
+ lnaband = 0xBB;
+ else if (freq <= 605000)
+ lnaband = 0xAA;
+ else if (freq <= 630000)
+ lnaband = 0x99;
+ else if (freq <= 655000)
+ lnaband = 0x88;
+ else if (freq <= 685000)
+ lnaband = 0x77;
+ else if (freq <= 710000)
+ lnaband = 0x66;
+ else if (freq <= 735000)
+ lnaband = 0x55;
+ else if (freq <= 765000)
+ lnaband = 0x44;
+ else if (freq <= 802000)
+ lnaband = 0x33;
+ else if (freq <= 840000)
+ lnaband = 0x22;
+ else
+ lnaband = 0x11;
b[0] = REG_TUNE;
b[1] = (tune >> 8) & 0x1F;
@@ -158,47 +209,54 @@ static int mt2266_set_params(struct dvb_frontend *fe, struct dvb_frontend_parame
b[3] = tune >> 13;
mt2266_writeregs(priv,b,4);
- dprintk("set_parms: tune=%d band=%d",(int)tune,(int)lnaband);
- dprintk("set_parms: [1..3]: %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3]);
-
- b[0] = 0x05;
- b[1] = 0x62;
- b[2] = lnaband;
- mt2266_writeregs(priv,b,3);
+ dprintk("set_parms: tune=%d band=%d %s",
+ (int) tune, (int) lnaband,
+ (band == MT2266_UHF) ? "UHF" : "VHF");
+ dprintk("set_parms: [1..3]: %2x %2x %2x",
+ (int) b[1], (int) b[2], (int)b[3]);
+
+ if (band == MT2266_UHF) {
+ b[0] = 0x05;
+ b[1] = (priv->band == MT2266_VHF) ? 0x52 : 0x62;
+ b[2] = lnaband;
+ mt2266_writeregs(priv, b, 3);
+ }
- //Waits for pll lock or timeout
+ /* Wait for pll lock or timeout */
i = 0;
do {
mt2266_readreg(priv,REG_LOCK,b);
- if ((b[0] & 0x40)==0x40)
+ if (b[0] & 0x40)
break;
msleep(10);
i++;
} while (i<10);
dprintk("Lock when i=%i",(int)i);
+
+ if (band == MT2266_UHF && priv->band == MT2266_VHF)
+ mt2266_writereg(priv, 0x05, 0x62);
+
+ priv->band = band;
+
return ret;
}
static void mt2266_calibrate(struct mt2266_priv *priv)
{
- mt2266_writereg(priv,0x11,0x03);
- mt2266_writereg(priv,0x11,0x01);
-
- mt2266_writeregs(priv,mt2266_init1,sizeof(mt2266_init1));
- mt2266_writeregs(priv,mt2266_init2,sizeof(mt2266_init2));
-
- mt2266_writereg(priv,0x33,0x5e);
- mt2266_writereg(priv,0x10,0x10);
- mt2266_writereg(priv,0x10,0x00);
-
- mt2266_writeregs(priv,mt2266_init_8mhz,sizeof(mt2266_init_8mhz));
-
+ mt2266_writereg(priv, 0x11, 0x03);
+ mt2266_writereg(priv, 0x11, 0x01);
+ mt2266_writeregs(priv, mt2266_init1, sizeof(mt2266_init1));
+ mt2266_writeregs(priv, mt2266_init2, sizeof(mt2266_init2));
+ mt2266_writereg(priv, 0x33, 0x5e);
+ mt2266_writereg(priv, 0x10, 0x10);
+ mt2266_writereg(priv, 0x10, 0x00);
+ mt2266_writeregs(priv, mt2266_init_8mhz, sizeof(mt2266_init_8mhz));
msleep(25);
- mt2266_writereg(priv,0x17,0x6d);
- mt2266_writereg(priv,0x1c,0x00);
+ mt2266_writereg(priv, 0x17, 0x6d);
+ mt2266_writereg(priv, 0x1c, 0x00);
msleep(75);
- mt2266_writereg(priv,0x17,0x6d);
- mt2266_writereg(priv,0x1c,0xff);
+ mt2266_writereg(priv, 0x17, 0x6d);
+ mt2266_writereg(priv, 0x1c, 0xff);
}
static int mt2266_get_frequency(struct dvb_frontend *fe, u32 *frequency)
@@ -217,17 +275,22 @@ static int mt2266_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
static int mt2266_init(struct dvb_frontend *fe)
{
+ int ret;
struct mt2266_priv *priv = fe->tuner_priv;
- mt2266_writereg(priv,0x17,0x6d);
- mt2266_writereg(priv,0x1c,0xff);
+ ret = mt2266_writereg(priv, 0x17, 0x6d);
+ if (ret < 0)
+ return ret;
+ ret = mt2266_writereg(priv, 0x1c, 0xff);
+ if (ret < 0)
+ return ret;
return 0;
}
static int mt2266_sleep(struct dvb_frontend *fe)
{
struct mt2266_priv *priv = fe->tuner_priv;
- mt2266_writereg(priv,0x17,0x6d);
- mt2266_writereg(priv,0x1c,0x00);
+ mt2266_writereg(priv, 0x17, 0x6d);
+ mt2266_writereg(priv, 0x1c, 0x00);
return 0;
}
@@ -241,8 +304,8 @@ static int mt2266_release(struct dvb_frontend *fe)
static const struct dvb_tuner_ops mt2266_tuner_ops = {
.info = {
.name = "Microtune MT2266",
- .frequency_min = 470000000,
- .frequency_max = 860000000,
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
.frequency_step = 50000,
},
.release = mt2266_release,
@@ -264,8 +327,9 @@ struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter
priv->cfg = cfg;
priv->i2c = i2c;
+ priv->band = MT2266_UHF;
- if (mt2266_readreg(priv,0,&id) != 0) {
+ if (mt2266_readreg(priv, 0, &id)) {
kfree(priv);
return NULL;
}
diff --git a/drivers/media/dvb/frontends/mt312.c b/drivers/media/dvb/frontends/mt312.c
index 0606b9a5b616..1638301fbd6e 100644
--- a/drivers/media/dvb/frontends/mt312.c
+++ b/drivers/media/dvb/frontends/mt312.c
@@ -37,9 +37,9 @@
struct mt312_state {
- struct i2c_adapter* i2c;
+ struct i2c_adapter *i2c;
/* configuration settings */
- const struct mt312_config* config;
+ const struct mt312_config *config;
struct dvb_frontend frontend;
u8 id;
@@ -49,14 +49,15 @@ struct mt312_state {
static int debug;
#define dprintk(args...) \
do { \
- if (debug) printk(KERN_DEBUG "mt312: " args); \
+ if (debug) \
+ printk(KERN_DEBUG "mt312: " args); \
} while (0)
#define MT312_SYS_CLK 90000000UL /* 90 MHz */
#define MT312_LPOWER_SYS_CLK 60000000UL /* 60 MHz */
#define MT312_PLL_CLK 10000000UL /* 10 MHz */
-static int mt312_read(struct mt312_state* state, const enum mt312_reg_addr reg,
+static int mt312_read(struct mt312_state *state, const enum mt312_reg_addr reg,
void *buf, const size_t count)
{
int ret;
@@ -79,7 +80,7 @@ static int mt312_read(struct mt312_state* state, const enum mt312_reg_addr reg,
return -EREMOTEIO;
}
- if(debug) {
+ if (debug) {
int i;
dprintk("R(%d):", reg & 0x7f);
for (i = 0; i < count; i++)
@@ -90,14 +91,14 @@ static int mt312_read(struct mt312_state* state, const enum mt312_reg_addr reg,
return 0;
}
-static int mt312_write(struct mt312_state* state, const enum mt312_reg_addr reg,
+static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg,
const void *src, const size_t count)
{
int ret;
u8 buf[count + 1];
struct i2c_msg msg;
- if(debug) {
+ if (debug) {
int i;
dprintk("W(%d):", reg & 0x7f);
for (i = 0; i < count; i++)
@@ -123,13 +124,13 @@ static int mt312_write(struct mt312_state* state, const enum mt312_reg_addr reg,
return 0;
}
-static inline int mt312_readreg(struct mt312_state* state,
+static inline int mt312_readreg(struct mt312_state *state,
const enum mt312_reg_addr reg, u8 *val)
{
return mt312_read(state, reg, val, 1);
}
-static inline int mt312_writereg(struct mt312_state* state,
+static inline int mt312_writereg(struct mt312_state *state,
const enum mt312_reg_addr reg, const u8 val)
{
return mt312_write(state, reg, &val, 1);
@@ -140,18 +141,19 @@ static inline u32 mt312_div(u32 a, u32 b)
return (a + (b / 2)) / b;
}
-static int mt312_reset(struct mt312_state* state, const u8 full)
+static int mt312_reset(struct mt312_state *state, const u8 full)
{
return mt312_writereg(state, RESET, full ? 0x80 : 0x40);
}
-static int mt312_get_inversion(struct mt312_state* state,
+static int mt312_get_inversion(struct mt312_state *state,
fe_spectral_inversion_t *i)
{
int ret;
u8 vit_mode;
- if ((ret = mt312_readreg(state, VIT_MODE, &vit_mode)) < 0)
+ ret = mt312_readreg(state, VIT_MODE, &vit_mode);
+ if (ret < 0)
return ret;
if (vit_mode & 0x80) /* auto inversion was used */
@@ -160,7 +162,7 @@ static int mt312_get_inversion(struct mt312_state* state,
return 0;
}
-static int mt312_get_symbol_rate(struct mt312_state* state, u32 *sr)
+static int mt312_get_symbol_rate(struct mt312_state *state, u32 *sr)
{
int ret;
u8 sym_rate_h;
@@ -169,37 +171,44 @@ static int mt312_get_symbol_rate(struct mt312_state* state, u32 *sr)
u16 monitor;
u8 buf[2];
- if ((ret = mt312_readreg(state, SYM_RATE_H, &sym_rate_h)) < 0)
+ ret = mt312_readreg(state, SYM_RATE_H, &sym_rate_h);
+ if (ret < 0)
return ret;
- if (sym_rate_h & 0x80) { /* symbol rate search was used */
- if ((ret = mt312_writereg(state, MON_CTRL, 0x03)) < 0)
+ if (sym_rate_h & 0x80) {
+ /* symbol rate search was used */
+ ret = mt312_writereg(state, MON_CTRL, 0x03);
+ if (ret < 0)
return ret;
- if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0)
+ ret = mt312_read(state, MONITOR_H, buf, sizeof(buf));
+ if (ret < 0)
return ret;
monitor = (buf[0] << 8) | buf[1];
- dprintk(KERN_DEBUG "sr(auto) = %u\n",
+ dprintk("sr(auto) = %u\n",
mt312_div(monitor * 15625, 4));
} else {
- if ((ret = mt312_writereg(state, MON_CTRL, 0x05)) < 0)
+ ret = mt312_writereg(state, MON_CTRL, 0x05);
+ if (ret < 0)
return ret;
- if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0)
+ ret = mt312_read(state, MONITOR_H, buf, sizeof(buf));
+ if (ret < 0)
return ret;
dec_ratio = ((buf[0] >> 5) & 0x07) * 32;
- if ((ret = mt312_read(state, SYM_RAT_OP_H, buf, sizeof(buf))) < 0)
+ ret = mt312_read(state, SYM_RAT_OP_H, buf, sizeof(buf));
+ if (ret < 0)
return ret;
sym_rat_op = (buf[0] << 8) | buf[1];
- dprintk(KERN_DEBUG "sym_rat_op=%d dec_ratio=%d\n",
+ dprintk("sym_rat_op=%d dec_ratio=%d\n",
sym_rat_op, dec_ratio);
- dprintk(KERN_DEBUG "*sr(manual) = %lu\n",
+ dprintk("*sr(manual) = %lu\n",
(((MT312_PLL_CLK * 8192) / (sym_rat_op + 8192)) *
2) - dec_ratio);
}
@@ -207,7 +216,7 @@ static int mt312_get_symbol_rate(struct mt312_state* state, u32 *sr)
return 0;
}
-static int mt312_get_code_rate(struct mt312_state* state, fe_code_rate_t *cr)
+static int mt312_get_code_rate(struct mt312_state *state, fe_code_rate_t *cr)
{
const fe_code_rate_t fec_tab[8] =
{ FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8,
@@ -216,7 +225,8 @@ static int mt312_get_code_rate(struct mt312_state* state, fe_code_rate_t *cr)
int ret;
u8 fec_status;
- if ((ret = mt312_readreg(state, FEC_STATUS, &fec_status)) < 0)
+ ret = mt312_readreg(state, FEC_STATUS, &fec_status);
+ if (ret < 0)
return ret;
*cr = fec_tab[(fec_status >> 4) & 0x07];
@@ -224,61 +234,72 @@ static int mt312_get_code_rate(struct mt312_state* state, fe_code_rate_t *cr)
return 0;
}
-static int mt312_initfe(struct dvb_frontend* fe)
+static int mt312_initfe(struct dvb_frontend *fe)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
u8 buf[2];
/* wake up */
- if ((ret = mt312_writereg(state, CONFIG, (state->frequency == 60 ? 0x88 : 0x8c))) < 0)
+ ret = mt312_writereg(state, CONFIG,
+ (state->frequency == 60 ? 0x88 : 0x8c));
+ if (ret < 0)
return ret;
/* wait at least 150 usec */
udelay(150);
/* full reset */
- if ((ret = mt312_reset(state, 1)) < 0)
+ ret = mt312_reset(state, 1);
+ if (ret < 0)
return ret;
-// Per datasheet, write correct values. 09/28/03 ACCJr.
-// If we don't do this, we won't get FE_HAS_VITERBI in the VP310.
+/* Per datasheet, write correct values. 09/28/03 ACCJr.
+ * If we don't do this, we won't get FE_HAS_VITERBI in the VP310. */
{
- u8 buf_def[8]={0x14, 0x12, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00};
+ u8 buf_def[8] = { 0x14, 0x12, 0x03, 0x02,
+ 0x01, 0x00, 0x00, 0x00 };
- if ((ret = mt312_write(state, VIT_SETUP, buf_def, sizeof(buf_def))) < 0)
+ ret = mt312_write(state, VIT_SETUP, buf_def, sizeof(buf_def));
+ if (ret < 0)
return ret;
}
/* SYS_CLK */
- buf[0] = mt312_div((state->frequency == 60 ? MT312_LPOWER_SYS_CLK : MT312_SYS_CLK) * 2, 1000000);
+ buf[0] = mt312_div((state->frequency == 60 ? MT312_LPOWER_SYS_CLK :
+ MT312_SYS_CLK) * 2, 1000000);
/* DISEQC_RATIO */
buf[1] = mt312_div(MT312_PLL_CLK, 15000 * 4);
- if ((ret = mt312_write(state, SYS_CLK, buf, sizeof(buf))) < 0)
+ ret = mt312_write(state, SYS_CLK, buf, sizeof(buf));
+ if (ret < 0)
return ret;
- if ((ret = mt312_writereg(state, SNR_THS_HIGH, 0x32)) < 0)
+ ret = mt312_writereg(state, SNR_THS_HIGH, 0x32);
+ if (ret < 0)
return ret;
- if ((ret = mt312_writereg(state, OP_CTRL, 0x53)) < 0)
+ ret = mt312_writereg(state, OP_CTRL, 0x53);
+ if (ret < 0)
return ret;
/* TS_SW_LIM */
buf[0] = 0x8c;
buf[1] = 0x98;
- if ((ret = mt312_write(state, TS_SW_LIM_L, buf, sizeof(buf))) < 0)
+ ret = mt312_write(state, TS_SW_LIM_L, buf, sizeof(buf));
+ if (ret < 0)
return ret;
- if ((ret = mt312_writereg(state, CS_SW_LIM, 0x69)) < 0)
+ ret = mt312_writereg(state, CS_SW_LIM, 0x69);
+ if (ret < 0)
return ret;
return 0;
}
-static int mt312_send_master_cmd(struct dvb_frontend* fe,
+static int mt312_send_master_cmd(struct dvb_frontend *fe,
struct dvb_diseqc_master_cmd *c)
{
struct mt312_state *state = fe->demodulator_priv;
@@ -288,29 +309,31 @@ static int mt312_send_master_cmd(struct dvb_frontend* fe,
if ((c->msg_len == 0) || (c->msg_len > sizeof(c->msg)))
return -EINVAL;
- if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
+ ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode);
+ if (ret < 0)
return ret;
- if ((ret =
- mt312_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len)) < 0)
+ ret = mt312_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len);
+ if (ret < 0)
return ret;
- if ((ret =
- mt312_writereg(state, DISEQC_MODE,
- (diseqc_mode & 0x40) | ((c->msg_len - 1) << 3)
- | 0x04)) < 0)
+ ret = mt312_writereg(state, DISEQC_MODE,
+ (diseqc_mode & 0x40) | ((c->msg_len - 1) << 3)
+ | 0x04);
+ if (ret < 0)
return ret;
/* set DISEQC_MODE[2:0] to zero if a return message is expected */
- if (c->msg[0] & 0x02)
- if ((ret =
- mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40))) < 0)
+ if (c->msg[0] & 0x02) {
+ ret = mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40));
+ if (ret < 0)
return ret;
+ }
return 0;
}
-static int mt312_send_burst(struct dvb_frontend* fe, const fe_sec_mini_cmd_t c)
+static int mt312_send_burst(struct dvb_frontend *fe, const fe_sec_mini_cmd_t c)
{
struct mt312_state *state = fe->demodulator_priv;
const u8 mini_tab[2] = { 0x02, 0x03 };
@@ -321,18 +344,19 @@ static int mt312_send_burst(struct dvb_frontend* fe, const fe_sec_mini_cmd_t c)
if (c > SEC_MINI_B)
return -EINVAL;
- if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
+ ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode);
+ if (ret < 0)
return ret;
- if ((ret =
- mt312_writereg(state, DISEQC_MODE,
- (diseqc_mode & 0x40) | mini_tab[c])) < 0)
+ ret = mt312_writereg(state, DISEQC_MODE,
+ (diseqc_mode & 0x40) | mini_tab[c]);
+ if (ret < 0)
return ret;
return 0;
}
-static int mt312_set_tone(struct dvb_frontend* fe, const fe_sec_tone_mode_t t)
+static int mt312_set_tone(struct dvb_frontend *fe, const fe_sec_tone_mode_t t)
{
struct mt312_state *state = fe->demodulator_priv;
const u8 tone_tab[2] = { 0x01, 0x00 };
@@ -343,18 +367,19 @@ static int mt312_set_tone(struct dvb_frontend* fe, const fe_sec_tone_mode_t t)
if (t > SEC_TONE_OFF)
return -EINVAL;
- if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
+ ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode);
+ if (ret < 0)
return ret;
- if ((ret =
- mt312_writereg(state, DISEQC_MODE,
- (diseqc_mode & 0x40) | tone_tab[t])) < 0)
+ ret = mt312_writereg(state, DISEQC_MODE,
+ (diseqc_mode & 0x40) | tone_tab[t]);
+ if (ret < 0)
return ret;
return 0;
}
-static int mt312_set_voltage(struct dvb_frontend* fe, const fe_sec_voltage_t v)
+static int mt312_set_voltage(struct dvb_frontend *fe, const fe_sec_voltage_t v)
{
struct mt312_state *state = fe->demodulator_priv;
const u8 volt_tab[3] = { 0x00, 0x40, 0x00 };
@@ -365,7 +390,7 @@ static int mt312_set_voltage(struct dvb_frontend* fe, const fe_sec_voltage_t v)
return mt312_writereg(state, DISEQC_MODE, volt_tab[v]);
}
-static int mt312_read_status(struct dvb_frontend* fe, fe_status_t *s)
+static int mt312_read_status(struct dvb_frontend *fe, fe_status_t *s)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
@@ -373,10 +398,12 @@ static int mt312_read_status(struct dvb_frontend* fe, fe_status_t *s)
*s = 0;
- if ((ret = mt312_read(state, QPSK_STAT_H, status, sizeof(status))) < 0)
+ ret = mt312_read(state, QPSK_STAT_H, status, sizeof(status));
+ if (ret < 0)
return ret;
- dprintk(KERN_DEBUG "QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x, FEC_STATUS: 0x%02x\n", status[0], status[1], status[2]);
+ dprintk("QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x,"
+ " FEC_STATUS: 0x%02x\n", status[0], status[1], status[2]);
if (status[0] & 0xc0)
*s |= FE_HAS_SIGNAL; /* signal noise ratio */
@@ -392,13 +419,14 @@ static int mt312_read_status(struct dvb_frontend* fe, fe_status_t *s)
return 0;
}
-static int mt312_read_ber(struct dvb_frontend* fe, u32 *ber)
+static int mt312_read_ber(struct dvb_frontend *fe, u32 *ber)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
u8 buf[3];
- if ((ret = mt312_read(state, RS_BERCNT_H, buf, 3)) < 0)
+ ret = mt312_read(state, RS_BERCNT_H, buf, 3);
+ if (ret < 0)
return ret;
*ber = ((buf[0] << 16) | (buf[1] << 8) | buf[2]) * 64;
@@ -406,7 +434,8 @@ static int mt312_read_ber(struct dvb_frontend* fe, u32 *ber)
return 0;
}
-static int mt312_read_signal_strength(struct dvb_frontend* fe, u16 *signal_strength)
+static int mt312_read_signal_strength(struct dvb_frontend *fe,
+ u16 *signal_strength)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
@@ -414,7 +443,8 @@ static int mt312_read_signal_strength(struct dvb_frontend* fe, u16 *signal_stren
u16 agc;
s16 err_db;
- if ((ret = mt312_read(state, AGC_H, buf, sizeof(buf))) < 0)
+ ret = mt312_read(state, AGC_H, buf, sizeof(buf));
+ if (ret < 0)
return ret;
agc = (buf[0] << 6) | (buf[1] >> 2);
@@ -422,18 +452,19 @@ static int mt312_read_signal_strength(struct dvb_frontend* fe, u16 *signal_stren
*signal_strength = agc;
- dprintk(KERN_DEBUG "agc=%08x err_db=%hd\n", agc, err_db);
+ dprintk("agc=%08x err_db=%hd\n", agc, err_db);
return 0;
}
-static int mt312_read_snr(struct dvb_frontend* fe, u16 *snr)
+static int mt312_read_snr(struct dvb_frontend *fe, u16 *snr)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
u8 buf[2];
- if ((ret = mt312_read(state, M_SNR_H, &buf, sizeof(buf))) < 0)
+ ret = mt312_read(state, M_SNR_H, &buf, sizeof(buf));
+ if (ret < 0)
return ret;
*snr = 0xFFFF - ((((buf[0] & 0x7f) << 8) | buf[1]) << 1);
@@ -441,13 +472,14 @@ static int mt312_read_snr(struct dvb_frontend* fe, u16 *snr)
return 0;
}
-static int mt312_read_ucblocks(struct dvb_frontend* fe, u32 *ubc)
+static int mt312_read_ucblocks(struct dvb_frontend *fe, u32 *ubc)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
u8 buf[2];
- if ((ret = mt312_read(state, RS_UBC_H, &buf, sizeof(buf))) < 0)
+ ret = mt312_read(state, RS_UBC_H, &buf, sizeof(buf));
+ if (ret < 0)
return ret;
*ubc = (buf[0] << 8) | buf[1];
@@ -455,7 +487,7 @@ static int mt312_read_ucblocks(struct dvb_frontend* fe, u32 *ubc)
return 0;
}
-static int mt312_set_frontend(struct dvb_frontend* fe,
+static int mt312_set_frontend(struct dvb_frontend *fe,
struct dvb_frontend_parameters *p)
{
struct mt312_state *state = fe->demodulator_priv;
@@ -491,24 +523,28 @@ static int mt312_set_frontend(struct dvb_frontend* fe,
switch (state->id) {
case ID_VP310:
- // For now we will do this only for the VP310.
- // It should be better for the mt312 as well, but tunning will be slower. ACCJr 09/29/03
+ /* For now we will do this only for the VP310.
+ * It should be better for the mt312 as well,
+ * but tuning will be slower. ACCJr 09/29/03
+ */
ret = mt312_readreg(state, CONFIG, &config_val);
if (ret < 0)
return ret;
- if (p->u.qpsk.symbol_rate >= 30000000) //Note that 30MS/s should use 90MHz
- {
- if ((config_val & 0x0c) == 0x08) { //We are running 60MHz
+ if (p->u.qpsk.symbol_rate >= 30000000) {
+ /* Note that 30MS/s should use 90MHz */
+ if ((config_val & 0x0c) == 0x08) {
+ /* We are running 60MHz */
state->frequency = 90;
- if ((ret = mt312_initfe(fe)) < 0)
+ ret = mt312_initfe(fe);
+ if (ret < 0)
return ret;
}
- }
- else
- {
- if ((config_val & 0x0c) == 0x0C) { //We are running 90MHz
+ } else {
+ if ((config_val & 0x0c) == 0x0C) {
+ /* We are running 90MHz */
state->frequency = 60;
- if ((ret = mt312_initfe(fe)) < 0)
+ ret = mt312_initfe(fe);
+ if (ret < 0)
return ret;
}
}
@@ -523,7 +559,8 @@ static int mt312_set_frontend(struct dvb_frontend* fe,
if (fe->ops.tuner_ops.set_params) {
fe->ops.tuner_ops.set_params(fe, p);
- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
}
/* sr = (u16)(sr * 256.0 / 1000000.0) */
@@ -545,7 +582,8 @@ static int mt312_set_frontend(struct dvb_frontend* fe,
/* GO */
buf[4] = 0x01;
- if ((ret = mt312_write(state, SYM_RATE_H, buf, sizeof(buf))) < 0)
+ ret = mt312_write(state, SYM_RATE_H, buf, sizeof(buf));
+ if (ret < 0)
return ret;
mt312_reset(state, 0);
@@ -553,27 +591,30 @@ static int mt312_set_frontend(struct dvb_frontend* fe,
return 0;
}
-static int mt312_get_frontend(struct dvb_frontend* fe,
+static int mt312_get_frontend(struct dvb_frontend *fe,
struct dvb_frontend_parameters *p)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
- if ((ret = mt312_get_inversion(state, &p->inversion)) < 0)
+ ret = mt312_get_inversion(state, &p->inversion);
+ if (ret < 0)
return ret;
- if ((ret = mt312_get_symbol_rate(state, &p->u.qpsk.symbol_rate)) < 0)
+ ret = mt312_get_symbol_rate(state, &p->u.qpsk.symbol_rate);
+ if (ret < 0)
return ret;
- if ((ret = mt312_get_code_rate(state, &p->u.qpsk.fec_inner)) < 0)
+ ret = mt312_get_code_rate(state, &p->u.qpsk.fec_inner);
+ if (ret < 0)
return ret;
return 0;
}
-static int mt312_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
+static int mt312_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
{
- struct mt312_state* state = fe->demodulator_priv;
+ struct mt312_state *state = fe->demodulator_priv;
if (enable) {
return mt312_writereg(state, GPP_CTRL, 0x40);
@@ -582,27 +623,31 @@ static int mt312_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
}
}
-static int mt312_sleep(struct dvb_frontend* fe)
+static int mt312_sleep(struct dvb_frontend *fe)
{
struct mt312_state *state = fe->demodulator_priv;
int ret;
u8 config;
/* reset all registers to defaults */
- if ((ret = mt312_reset(state, 1)) < 0)
+ ret = mt312_reset(state, 1);
+ if (ret < 0)
return ret;
- if ((ret = mt312_readreg(state, CONFIG, &config)) < 0)
+ ret = mt312_readreg(state, CONFIG, &config);
+ if (ret < 0)
return ret;
/* enter standby */
- if ((ret = mt312_writereg(state, CONFIG, config & 0x7f)) < 0)
+ ret = mt312_writereg(state, CONFIG, config & 0x7f);
+ if (ret < 0)
return ret;
return 0;
}
-static int mt312_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+static int mt312_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
{
fesettings->min_delay_ms = 50;
fesettings->step_size = 0;
@@ -610,9 +655,9 @@ static int mt312_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_
return 0;
}
-static void mt312_release(struct dvb_frontend* fe)
+static void mt312_release(struct dvb_frontend *fe)
{
- struct mt312_state* state = fe->demodulator_priv;
+ struct mt312_state *state = fe->demodulator_priv;
kfree(state);
}
@@ -655,10 +700,10 @@ 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 i2c_adapter* i2c)
+struct dvb_frontend *vp310_mt312_attach(const struct mt312_config *config,
+ struct i2c_adapter *i2c)
{
- struct mt312_state* state = NULL;
+ struct mt312_state *state = NULL;
/* allocate memory for the internal state */
state = kmalloc(sizeof(struct mt312_state), GFP_KERNEL);
@@ -674,7 +719,8 @@ struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
goto error;
/* create dvb_frontend */
- memcpy(&state->frontend.ops, &vp310_mt312_ops, sizeof(struct dvb_frontend_ops));
+ memcpy(&state->frontend.ops, &vp310_mt312_ops,
+ sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
switch (state->id) {
@@ -687,7 +733,8 @@ struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
state->frequency = 60;
break;
default:
- printk (KERN_WARNING "Only Zarlink VP310/MT312 are supported chips.\n");
+ printk(KERN_WARNING "Only Zarlink VP310/MT312"
+ " are supported chips.\n");
goto error;
}
@@ -697,6 +744,7 @@ error:
kfree(state);
return NULL;
}
+EXPORT_SYMBOL(vp310_mt312_attach);
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
@@ -705,4 +753,3 @@ MODULE_DESCRIPTION("Zarlink VP310/MT312 DVB-S Demodulator driver");
MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(vp310_mt312_attach);
diff --git a/drivers/media/dvb/frontends/mt312.h b/drivers/media/dvb/frontends/mt312.h
index cf9a1505ad4b..f17cb93ba9ba 100644
--- a/drivers/media/dvb/frontends/mt312.h
+++ b/drivers/media/dvb/frontends/mt312.h
@@ -28,22 +28,21 @@
#include <linux/dvb/frontend.h>
-struct mt312_config
-{
+struct mt312_config {
/* the demodulator's i2c address */
u8 demod_address;
};
#if defined(CONFIG_DVB_MT312) || (defined(CONFIG_DVB_MT312_MODULE) && defined(MODULE))
-struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
- struct i2c_adapter* i2c);
+struct dvb_frontend *vp310_mt312_attach(const struct mt312_config *config,
+ struct i2c_adapter *i2c);
#else
-static inline struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
- struct i2c_adapter* i2c)
+static inline struct dvb_frontend *vp310_mt312_attach(
+ const struct mt312_config *config, struct i2c_adapter *i2c)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
return NULL;
}
-#endif // CONFIG_DVB_MT312
+#endif /* CONFIG_DVB_MT312 */
-#endif // MT312_H
+#endif /* MT312_H */
diff --git a/drivers/media/dvb/frontends/mt352.c b/drivers/media/dvb/frontends/mt352.c
index 5dd9b731f6f2..7cd190b6f015 100644
--- a/drivers/media/dvb/frontends/mt352.c
+++ b/drivers/media/dvb/frontends/mt352.c
@@ -152,7 +152,13 @@ static void mt352_calc_input_freq(struct mt352_state* state,
if (state->config.if2)
if2 = state->config.if2;
- ife = (2*adc_clock - if2);
+ if (adc_clock >= if2 * 2)
+ ife = if2;
+ else {
+ ife = adc_clock - (if2 % adc_clock);
+ if (ife > adc_clock / 2)
+ ife = adc_clock - ife;
+ }
value = -16374 * ife / adc_clock;
dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n",
__FUNCTION__, if2, ife, adc_clock, value, value & 0x3fff);
diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c
index b314a1f2deed..1d2d28ce823d 100644
--- a/drivers/media/dvb/frontends/or51132.c
+++ b/drivers/media/dvb/frontends/or51132.c
@@ -564,7 +564,7 @@ struct dvb_frontend* or51132_attach(const struct or51132_config* config,
/* Allocate memory for the internal state */
state = kmalloc(sizeof(struct or51132_state), GFP_KERNEL);
if (state == NULL)
- goto error;
+ return NULL;
/* Setup the state */
state->config = config;
@@ -576,10 +576,6 @@ struct dvb_frontend* or51132_attach(const struct or51132_config* config,
memcpy(&state->frontend.ops, &or51132_ops, sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
return &state->frontend;
-
-error:
- kfree(state);
- return NULL;
}
static struct dvb_frontend_ops or51132_ops = {
diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c
index f02bd9445955..6a6b0d727c6f 100644
--- a/drivers/media/dvb/frontends/or51211.c
+++ b/drivers/media/dvb/frontends/or51211.c
@@ -529,7 +529,7 @@ struct dvb_frontend* or51211_attach(const struct or51211_config* config,
/* Allocate memory for the internal state */
state = kmalloc(sizeof(struct or51211_state), GFP_KERNEL);
if (state == NULL)
- goto error;
+ return NULL;
/* Setup the state */
state->config = config;
@@ -541,10 +541,6 @@ struct dvb_frontend* or51211_attach(const struct or51211_config* config,
memcpy(&state->frontend.ops, &or51211_ops, sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
return &state->frontend;
-
-error:
- kfree(state);
- return NULL;
}
static struct dvb_frontend_ops or51211_ops = {
diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb/frontends/s5h1409.c
index 562d9208857a..819433485d3b 100644
--- a/drivers/media/dvb/frontends/s5h1409.c
+++ b/drivers/media/dvb/frontends/s5h1409.c
@@ -42,6 +42,7 @@ struct s5h1409_state {
fe_modulation_t current_modulation;
u32 current_frequency;
+ int if_freq;
u32 is_qam_locked;
u32 qam_state;
@@ -97,7 +98,7 @@ static struct init_tab {
{ 0xac, 0x1003, },
{ 0xad, 0x103f, },
{ 0xe2, 0x0100, },
- { 0xe3, 0x0000, },
+ { 0xe3, 0x1000, },
{ 0x28, 0x1010, },
{ 0xb1, 0x000e, },
};
@@ -348,28 +349,32 @@ static int s5h1409_softreset(struct dvb_frontend* fe)
return 0;
}
+#define S5H1409_VSB_IF_FREQ 5380
+#define S5H1409_QAM_IF_FREQ state->config->qam_if
+
static int s5h1409_set_if_freq(struct dvb_frontend* fe, int KHz)
{
struct s5h1409_state* state = fe->demodulator_priv;
- int ret = 0;
dprintk("%s(%d KHz)\n", __FUNCTION__, KHz);
- if( (KHz == 44000) || (KHz == 5380) ) {
- s5h1409_writereg(state, 0x87, 0x01be);
- s5h1409_writereg(state, 0x88, 0x0436);
- s5h1409_writereg(state, 0x89, 0x054d);
- } else
- if (KHz == 4000) {
+ switch (KHz) {
+ case 4000:
s5h1409_writereg(state, 0x87, 0x014b);
s5h1409_writereg(state, 0x88, 0x0cb5);
s5h1409_writereg(state, 0x89, 0x03e2);
- } else {
- printk("%s() Invalid arg = %d KHz\n", __FUNCTION__, KHz);
- ret = -1;
+ break;
+ case 5380:
+ case 44000:
+ default:
+ s5h1409_writereg(state, 0x87, 0x01be);
+ s5h1409_writereg(state, 0x88, 0x0436);
+ s5h1409_writereg(state, 0x89, 0x054d);
+ break;
}
+ state->if_freq = KHz;
- return ret;
+ return 0;
}
static int s5h1409_set_spectralinversion(struct dvb_frontend* fe, int inverted)
@@ -394,11 +399,15 @@ static int s5h1409_enable_modulation(struct dvb_frontend* fe,
switch(m) {
case VSB_8:
dprintk("%s() VSB_8\n", __FUNCTION__);
+ if (state->if_freq != S5H1409_VSB_IF_FREQ)
+ s5h1409_set_if_freq(fe, S5H1409_VSB_IF_FREQ);
s5h1409_writereg(state, 0xf4, 0);
break;
case QAM_64:
case QAM_256:
dprintk("%s() QAM_AUTO (64/256)\n", __FUNCTION__);
+ if (state->if_freq != S5H1409_QAM_IF_FREQ)
+ s5h1409_set_if_freq(fe, S5H1409_QAM_IF_FREQ);
s5h1409_writereg(state, 0xf4, 1);
s5h1409_writereg(state, 0x85, 0x110);
break;
@@ -432,9 +441,11 @@ static int s5h1409_set_gpio(struct dvb_frontend* fe, int enable)
dprintk("%s(%d)\n", __FUNCTION__, enable);
if (enable)
- return s5h1409_writereg(state, 0xe3, 0x1100);
+ return s5h1409_writereg(state, 0xe3,
+ s5h1409_readreg(state, 0xe3) | 0x1100);
else
- return s5h1409_writereg(state, 0xe3, 0x1000);
+ return s5h1409_writereg(state, 0xe3,
+ s5h1409_readreg(state, 0xe3) & 0xeeff);
}
static int s5h1409_sleep(struct dvb_frontend* fe, int enable)
@@ -504,13 +515,15 @@ static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe)
s5h1409_writereg(state, 0x96, 0x20);
s5h1409_writereg(state, 0xad,
( ((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff)) );
- s5h1409_writereg(state, 0xab, 0x1100);
+ s5h1409_writereg(state, 0xab,
+ s5h1409_readreg(state, 0xab) & 0xeffe);
}
} else {
if (state->qam_state != 1) {
state->qam_state = 1;
s5h1409_writereg(state, 0x96, 0x08);
- s5h1409_writereg(state, 0xab, 0x1101);
+ s5h1409_writereg(state, 0xab,
+ s5h1409_readreg(state, 0xab) | 0x1001);
}
}
}
@@ -547,6 +560,36 @@ static int s5h1409_set_frontend (struct dvb_frontend* fe,
return 0;
}
+static int s5h1409_set_mpeg_timing(struct dvb_frontend *fe, int mode)
+{
+ struct s5h1409_state *state = fe->demodulator_priv;
+ u16 val;
+
+ dprintk("%s(%d)\n", __FUNCTION__, mode);
+
+ val = s5h1409_readreg(state, 0xac) & 0xcfff;
+ switch (mode) {
+ case S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK:
+ val |= 0x0000;
+ break;
+ case S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK:
+ dprintk("%s(%d) Mode1 or Defaulting\n", __FUNCTION__, mode);
+ val |= 0x1000;
+ break;
+ case S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK:
+ val |= 0x2000;
+ break;
+ case S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK:
+ val |= 0x3000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Configure MPEG Signal Timing charactistics */
+ return s5h1409_writereg(state, 0xac, val);
+}
+
/* Reset the demod hardware and reset all of the configuration registers
to a default state. */
static int s5h1409_init (struct dvb_frontend* fe)
@@ -566,13 +609,16 @@ static int s5h1409_init (struct dvb_frontend* fe)
state->current_modulation = VSB_8;
if (state->config->output_mode == S5H1409_SERIAL_OUTPUT)
- s5h1409_writereg(state, 0xab, 0x100); /* Serial */
+ s5h1409_writereg(state, 0xab,
+ s5h1409_readreg(state, 0xab) | 0x100); /* Serial */
else
- s5h1409_writereg(state, 0xab, 0x0); /* Parallel */
+ s5h1409_writereg(state, 0xab,
+ s5h1409_readreg(state, 0xab) & 0xfeff); /* Parallel */
s5h1409_set_spectralinversion(fe, state->config->inversion);
- s5h1409_set_if_freq(fe, state->config->if_freq);
+ s5h1409_set_if_freq(fe, state->if_freq);
s5h1409_set_gpio(fe, state->config->gpio);
+ s5h1409_set_mpeg_timing(fe, state->config->mpeg_timing);
s5h1409_softreset(fe);
/* Note: Leaving the I2C gate closed. */
@@ -741,6 +787,7 @@ struct dvb_frontend* s5h1409_attach(const struct s5h1409_config* config,
struct i2c_adapter* i2c)
{
struct s5h1409_state* state = NULL;
+ u16 reg;
/* allocate memory for the internal state */
state = kmalloc(sizeof(struct s5h1409_state), GFP_KERNEL);
@@ -751,9 +798,11 @@ struct dvb_frontend* s5h1409_attach(const struct s5h1409_config* config,
state->config = config;
state->i2c = i2c;
state->current_modulation = 0;
+ state->if_freq = S5H1409_VSB_IF_FREQ;
/* check if the demod exists */
- if (s5h1409_readreg(state, 0x04) != 0x0066)
+ reg = s5h1409_readreg(state, 0x04);
+ if ((reg != 0x0066) && (reg != 0x007f))
goto error;
/* create dvb_frontend */
@@ -761,8 +810,14 @@ struct dvb_frontend* s5h1409_attach(const struct s5h1409_config* config,
sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
+ if (s5h1409_init(&state->frontend) != 0) {
+ printk(KERN_ERR "%s: Failed to initialize correctly\n",
+ __FUNCTION__);
+ goto error;
+ }
+
/* Note: Leaving the I2C gate open here. */
- s5h1409_writereg(state, 0xf3, 1);
+ s5h1409_i2c_gate_ctrl(&state->frontend, 1);
return &state->frontend;
diff --git a/drivers/media/dvb/frontends/s5h1409.h b/drivers/media/dvb/frontends/s5h1409.h
index 20f9af1af445..f0bb13fe808b 100644
--- a/drivers/media/dvb/frontends/s5h1409.h
+++ b/drivers/media/dvb/frontends/s5h1409.h
@@ -39,8 +39,8 @@ struct s5h1409_config
#define S5H1409_GPIO_ON 1
u8 gpio;
- /* IF Freq in KHz */
- u16 if_freq;
+ /* IF Freq for QAM in KHz, VSB is hardcoded to 5380 */
+ u16 qam_if;
/* Spectral Inversion */
#define S5H1409_INVERSION_OFF 0
@@ -51,6 +51,13 @@ struct s5h1409_config
#define S5H1409_TUNERLOCKING 0
#define S5H1409_DEMODLOCKING 1
u8 status_mode;
+
+ /* MPEG signal timing */
+#define S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0
+#define S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1
+#define S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2
+#define S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3
+ u16 mpeg_timing;
};
#if defined(CONFIG_DVB_S5H1409) || (defined(CONFIG_DVB_S5H1409_MODULE) && defined(MODULE))
diff --git a/drivers/media/dvb/frontends/tda18271-common.c b/drivers/media/dvb/frontends/tda18271-common.c
new file mode 100644
index 000000000000..cebb6b90b7e0
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda18271-common.c
@@ -0,0 +1,653 @@
+/*
+ tda18271-common.c - driver for the Philips / NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "tda18271-priv.h"
+
+static int tda18271_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ enum tda18271_i2c_gate gate;
+ int ret = 0;
+
+ switch (priv->gate) {
+ case TDA18271_GATE_DIGITAL:
+ case TDA18271_GATE_ANALOG:
+ gate = priv->gate;
+ break;
+ case TDA18271_GATE_AUTO:
+ default:
+ switch (priv->mode) {
+ case TDA18271_DIGITAL:
+ gate = TDA18271_GATE_DIGITAL;
+ break;
+ case TDA18271_ANALOG:
+ default:
+ gate = TDA18271_GATE_ANALOG;
+ break;
+ }
+ }
+
+ switch (gate) {
+ case TDA18271_GATE_ANALOG:
+ if (fe->ops.analog_ops.i2c_gate_ctrl)
+ ret = fe->ops.analog_ops.i2c_gate_ctrl(fe, enable);
+ break;
+ case TDA18271_GATE_DIGITAL:
+ if (fe->ops.i2c_gate_ctrl)
+ ret = fe->ops.i2c_gate_ctrl(fe, enable);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+};
+
+/*---------------------------------------------------------------------*/
+
+static void tda18271_dump_regs(struct dvb_frontend *fe, int extended)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ tda_reg("=== TDA18271 REG DUMP ===\n");
+ tda_reg("ID_BYTE = 0x%02x\n", 0xff & regs[R_ID]);
+ tda_reg("THERMO_BYTE = 0x%02x\n", 0xff & regs[R_TM]);
+ tda_reg("POWER_LEVEL_BYTE = 0x%02x\n", 0xff & regs[R_PL]);
+ tda_reg("EASY_PROG_BYTE_1 = 0x%02x\n", 0xff & regs[R_EP1]);
+ tda_reg("EASY_PROG_BYTE_2 = 0x%02x\n", 0xff & regs[R_EP2]);
+ tda_reg("EASY_PROG_BYTE_3 = 0x%02x\n", 0xff & regs[R_EP3]);
+ tda_reg("EASY_PROG_BYTE_4 = 0x%02x\n", 0xff & regs[R_EP4]);
+ tda_reg("EASY_PROG_BYTE_5 = 0x%02x\n", 0xff & regs[R_EP5]);
+ tda_reg("CAL_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_CPD]);
+ tda_reg("CAL_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_CD1]);
+ tda_reg("CAL_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_CD2]);
+ tda_reg("CAL_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_CD3]);
+ tda_reg("MAIN_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_MPD]);
+ tda_reg("MAIN_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_MD1]);
+ tda_reg("MAIN_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_MD2]);
+ tda_reg("MAIN_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_MD3]);
+
+ /* only dump extended regs if DBG_ADV is set */
+ if (!(tda18271_debug & DBG_ADV))
+ return;
+
+ /* W indicates write-only registers.
+ * Register dump for write-only registers shows last value written. */
+
+ tda_reg("EXTENDED_BYTE_1 = 0x%02x\n", 0xff & regs[R_EB1]);
+ tda_reg("EXTENDED_BYTE_2 = 0x%02x\n", 0xff & regs[R_EB2]);
+ tda_reg("EXTENDED_BYTE_3 = 0x%02x\n", 0xff & regs[R_EB3]);
+ tda_reg("EXTENDED_BYTE_4 = 0x%02x\n", 0xff & regs[R_EB4]);
+ tda_reg("EXTENDED_BYTE_5 = 0x%02x\n", 0xff & regs[R_EB5]);
+ tda_reg("EXTENDED_BYTE_6 = 0x%02x\n", 0xff & regs[R_EB6]);
+ tda_reg("EXTENDED_BYTE_7 = 0x%02x\n", 0xff & regs[R_EB7]);
+ tda_reg("EXTENDED_BYTE_8 = 0x%02x\n", 0xff & regs[R_EB8]);
+ tda_reg("EXTENDED_BYTE_9 W = 0x%02x\n", 0xff & regs[R_EB9]);
+ tda_reg("EXTENDED_BYTE_10 = 0x%02x\n", 0xff & regs[R_EB10]);
+ tda_reg("EXTENDED_BYTE_11 = 0x%02x\n", 0xff & regs[R_EB11]);
+ tda_reg("EXTENDED_BYTE_12 = 0x%02x\n", 0xff & regs[R_EB12]);
+ tda_reg("EXTENDED_BYTE_13 = 0x%02x\n", 0xff & regs[R_EB13]);
+ tda_reg("EXTENDED_BYTE_14 = 0x%02x\n", 0xff & regs[R_EB14]);
+ tda_reg("EXTENDED_BYTE_15 = 0x%02x\n", 0xff & regs[R_EB15]);
+ tda_reg("EXTENDED_BYTE_16 W = 0x%02x\n", 0xff & regs[R_EB16]);
+ tda_reg("EXTENDED_BYTE_17 W = 0x%02x\n", 0xff & regs[R_EB17]);
+ tda_reg("EXTENDED_BYTE_18 = 0x%02x\n", 0xff & regs[R_EB18]);
+ tda_reg("EXTENDED_BYTE_19 W = 0x%02x\n", 0xff & regs[R_EB19]);
+ tda_reg("EXTENDED_BYTE_20 W = 0x%02x\n", 0xff & regs[R_EB20]);
+ tda_reg("EXTENDED_BYTE_21 = 0x%02x\n", 0xff & regs[R_EB21]);
+ tda_reg("EXTENDED_BYTE_22 = 0x%02x\n", 0xff & regs[R_EB22]);
+ tda_reg("EXTENDED_BYTE_23 = 0x%02x\n", 0xff & regs[R_EB23]);
+}
+
+int tda18271_read_regs(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ unsigned char buf = 0x00;
+ int ret;
+ struct i2c_msg msg[] = {
+ { .addr = priv->i2c_addr, .flags = 0,
+ .buf = &buf, .len = 1 },
+ { .addr = priv->i2c_addr, .flags = I2C_M_RD,
+ .buf = regs, .len = 16 }
+ };
+
+ tda18271_i2c_gate_ctrl(fe, 1);
+
+ /* read all registers */
+ ret = i2c_transfer(priv->i2c_adap, msg, 2);
+
+ tda18271_i2c_gate_ctrl(fe, 0);
+
+ if (ret != 2)
+ tda_err("ERROR: i2c_transfer returned: %d\n", ret);
+
+ if (tda18271_debug & DBG_REG)
+ tda18271_dump_regs(fe, 0);
+
+ return (ret == 2 ? 0 : ret);
+}
+
+int tda18271_read_extended(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ unsigned char regdump[TDA18271_NUM_REGS];
+ unsigned char buf = 0x00;
+ int ret, i;
+ struct i2c_msg msg[] = {
+ { .addr = priv->i2c_addr, .flags = 0,
+ .buf = &buf, .len = 1 },
+ { .addr = priv->i2c_addr, .flags = I2C_M_RD,
+ .buf = regdump, .len = TDA18271_NUM_REGS }
+ };
+
+ tda18271_i2c_gate_ctrl(fe, 1);
+
+ /* read all registers */
+ ret = i2c_transfer(priv->i2c_adap, msg, 2);
+
+ tda18271_i2c_gate_ctrl(fe, 0);
+
+ if (ret != 2)
+ tda_err("ERROR: i2c_transfer returned: %d\n", ret);
+
+ for (i = 0; i <= TDA18271_NUM_REGS; i++) {
+ /* don't update write-only registers */
+ if ((i != R_EB9) &&
+ (i != R_EB16) &&
+ (i != R_EB17) &&
+ (i != R_EB19) &&
+ (i != R_EB20))
+ regs[i] = regdump[i];
+ }
+
+ if (tda18271_debug & DBG_REG)
+ tda18271_dump_regs(fe, 1);
+
+ return (ret == 2 ? 0 : ret);
+}
+
+int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ unsigned char buf[TDA18271_NUM_REGS + 1];
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = buf, .len = len + 1 };
+ int i, ret;
+
+ BUG_ON((len == 0) || (idx + len > sizeof(buf)));
+
+ buf[0] = idx;
+ for (i = 1; i <= len; i++)
+ buf[i] = regs[idx - 1 + i];
+
+ tda18271_i2c_gate_ctrl(fe, 1);
+
+ /* write registers */
+ ret = i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tda18271_i2c_gate_ctrl(fe, 0);
+
+ if (ret != 1)
+ tda_err("ERROR: i2c_transfer returned: %d\n", ret);
+
+ return (ret == 1 ? 0 : ret);
+}
+
+/*---------------------------------------------------------------------*/
+
+int tda18271_init_regs(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ tda_dbg("initializing registers for device @ %d-%04x\n",
+ i2c_adapter_id(priv->i2c_adap), priv->i2c_addr);
+
+ /* initialize registers */
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_ID] = 0x83;
+ break;
+ case TDA18271HDC2:
+ regs[R_ID] = 0x84;
+ break;
+ };
+
+ regs[R_TM] = 0x08;
+ regs[R_PL] = 0x80;
+ regs[R_EP1] = 0xc6;
+ regs[R_EP2] = 0xdf;
+ regs[R_EP3] = 0x16;
+ regs[R_EP4] = 0x60;
+ regs[R_EP5] = 0x80;
+ regs[R_CPD] = 0x80;
+ regs[R_CD1] = 0x00;
+ regs[R_CD2] = 0x00;
+ regs[R_CD3] = 0x00;
+ regs[R_MPD] = 0x00;
+ regs[R_MD1] = 0x00;
+ regs[R_MD2] = 0x00;
+ regs[R_MD3] = 0x00;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_EB1] = 0xff;
+ break;
+ case TDA18271HDC2:
+ regs[R_EB1] = 0xfc;
+ break;
+ };
+
+ regs[R_EB2] = 0x01;
+ regs[R_EB3] = 0x84;
+ regs[R_EB4] = 0x41;
+ regs[R_EB5] = 0x01;
+ regs[R_EB6] = 0x84;
+ regs[R_EB7] = 0x40;
+ regs[R_EB8] = 0x07;
+ regs[R_EB9] = 0x00;
+ regs[R_EB10] = 0x00;
+ regs[R_EB11] = 0x96;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_EB12] = 0x0f;
+ break;
+ case TDA18271HDC2:
+ regs[R_EB12] = 0x33;
+ break;
+ };
+
+ regs[R_EB13] = 0xc1;
+ regs[R_EB14] = 0x00;
+ regs[R_EB15] = 0x8f;
+ regs[R_EB16] = 0x00;
+ regs[R_EB17] = 0x00;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_EB18] = 0x00;
+ break;
+ case TDA18271HDC2:
+ regs[R_EB18] = 0x8c;
+ break;
+ };
+
+ regs[R_EB19] = 0x00;
+ regs[R_EB20] = 0x20;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_EB21] = 0x33;
+ break;
+ case TDA18271HDC2:
+ regs[R_EB21] = 0xb3;
+ break;
+ };
+
+ regs[R_EB22] = 0x48;
+ regs[R_EB23] = 0xb0;
+
+ tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS);
+
+ /* setup agc1 gain */
+ regs[R_EB17] = 0x00;
+ tda18271_write_regs(fe, R_EB17, 1);
+ regs[R_EB17] = 0x03;
+ tda18271_write_regs(fe, R_EB17, 1);
+ regs[R_EB17] = 0x43;
+ tda18271_write_regs(fe, R_EB17, 1);
+ regs[R_EB17] = 0x4c;
+ tda18271_write_regs(fe, R_EB17, 1);
+
+ /* setup agc2 gain */
+ if ((priv->id) == TDA18271HDC1) {
+ regs[R_EB20] = 0xa0;
+ tda18271_write_regs(fe, R_EB20, 1);
+ regs[R_EB20] = 0xa7;
+ tda18271_write_regs(fe, R_EB20, 1);
+ regs[R_EB20] = 0xe7;
+ tda18271_write_regs(fe, R_EB20, 1);
+ regs[R_EB20] = 0xec;
+ tda18271_write_regs(fe, R_EB20, 1);
+ }
+
+ /* image rejection calibration */
+
+ /* low-band */
+ regs[R_EP3] = 0x1f;
+ regs[R_EP4] = 0x66;
+ regs[R_EP5] = 0x81;
+ regs[R_CPD] = 0xcc;
+ regs[R_CD1] = 0x6c;
+ regs[R_CD2] = 0x00;
+ regs[R_CD3] = 0x00;
+ regs[R_MPD] = 0xcd;
+ regs[R_MD1] = 0x77;
+ regs[R_MD2] = 0x08;
+ regs[R_MD3] = 0x00;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ tda18271_write_regs(fe, R_EP3, 11);
+ break;
+ case TDA18271HDC2:
+ tda18271_write_regs(fe, R_EP3, 12);
+ break;
+ };
+
+ if ((priv->id) == TDA18271HDC2) {
+ /* main pll cp source on */
+ regs[R_EB4] = 0x61;
+ tda18271_write_regs(fe, R_EB4, 1);
+ msleep(1);
+
+ /* main pll cp source off */
+ regs[R_EB4] = 0x41;
+ tda18271_write_regs(fe, R_EB4, 1);
+ }
+
+ msleep(5); /* pll locking */
+
+ /* launch detector */
+ tda18271_write_regs(fe, R_EP1, 1);
+ msleep(5); /* wanted low measurement */
+
+ regs[R_EP5] = 0x85;
+ regs[R_CPD] = 0xcb;
+ regs[R_CD1] = 0x66;
+ regs[R_CD2] = 0x70;
+
+ tda18271_write_regs(fe, R_EP3, 7);
+ msleep(5); /* pll locking */
+
+ /* launch optimization algorithm */
+ tda18271_write_regs(fe, R_EP2, 1);
+ msleep(30); /* image low optimization completion */
+
+ /* mid-band */
+ regs[R_EP5] = 0x82;
+ regs[R_CPD] = 0xa8;
+ regs[R_CD2] = 0x00;
+ regs[R_MPD] = 0xa9;
+ regs[R_MD1] = 0x73;
+ regs[R_MD2] = 0x1a;
+
+ tda18271_write_regs(fe, R_EP3, 11);
+ msleep(5); /* pll locking */
+
+ tda18271_write_regs(fe, R_EP1, 1);
+ msleep(5); /* wanted mid measurement */
+
+ regs[R_EP5] = 0x86;
+ regs[R_CPD] = 0xa8;
+ regs[R_CD1] = 0x66;
+ regs[R_CD2] = 0xa0;
+
+ tda18271_write_regs(fe, R_EP3, 7);
+ msleep(5); /* pll locking */
+
+ /* launch optimization algorithm */
+ tda18271_write_regs(fe, R_EP2, 1);
+ msleep(30); /* image mid optimization completion */
+
+ /* high-band */
+ regs[R_EP5] = 0x83;
+ regs[R_CPD] = 0x98;
+ regs[R_CD1] = 0x65;
+ regs[R_CD2] = 0x00;
+ regs[R_MPD] = 0x99;
+ regs[R_MD1] = 0x71;
+ regs[R_MD2] = 0xcd;
+
+ tda18271_write_regs(fe, R_EP3, 11);
+ msleep(5); /* pll locking */
+
+ /* launch detector */
+ tda18271_write_regs(fe, R_EP1, 1);
+ msleep(5); /* wanted high measurement */
+
+ regs[R_EP5] = 0x87;
+ regs[R_CD1] = 0x65;
+ regs[R_CD2] = 0x50;
+
+ tda18271_write_regs(fe, R_EP3, 7);
+ msleep(5); /* pll locking */
+
+ /* launch optimization algorithm */
+ tda18271_write_regs(fe, R_EP2, 1);
+ msleep(30); /* image high optimization completion */
+
+ /* return to normal mode */
+ regs[R_EP4] = 0x64;
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ /* synchronize */
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+/*
+ * Standby modes, EP3 [7:5]
+ *
+ * | SM || SM_LT || SM_XT || mode description
+ * |=====\\=======\\=======\\===================================
+ * | 0 || 0 || 0 || normal mode
+ * |-----||-------||-------||-----------------------------------
+ * | || || || standby mode w/ slave tuner output
+ * | 1 || 0 || 0 || & loop thru & xtal oscillator on
+ * |-----||-------||-------||-----------------------------------
+ * | 1 || 1 || 0 || standby mode w/ xtal oscillator on
+ * |-----||-------||-------||-----------------------------------
+ * | 1 || 1 || 1 || power off
+ *
+ */
+
+int tda18271_set_standby_mode(struct dvb_frontend *fe,
+ int sm, int sm_lt, int sm_xt)
+{
+ 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);
+
+ 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;
+}
+
+/*---------------------------------------------------------------------*/
+
+int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq)
+{
+ /* sets main post divider & divider bytes, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 d, pd;
+ u32 div;
+
+ int ret = tda18271_lookup_pll_map(fe, MAIN_PLL, &freq, &pd, &d);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_MPD] = (0x77 & pd);
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ regs[R_MPD] &= ~0x08;
+ break;
+ case TDA18271_DIGITAL:
+ regs[R_MPD] |= 0x08;
+ break;
+ }
+
+ div = ((d * (freq / 1000)) << 7) / 125;
+
+ regs[R_MD1] = 0x7f & (div >> 16);
+ regs[R_MD2] = 0xff & (div >> 8);
+ regs[R_MD3] = 0xff & div;
+fail:
+ return ret;
+}
+
+int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq)
+{
+ /* sets cal post divider & divider bytes, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 d, pd;
+ u32 div;
+
+ int ret = tda18271_lookup_pll_map(fe, CAL_PLL, &freq, &pd, &d);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_CPD] = pd;
+
+ div = ((d * (freq / 1000)) << 7) / 125;
+
+ regs[R_CD1] = 0x7f & (div >> 16);
+ regs[R_CD2] = 0xff & (div >> 8);
+ regs[R_CD3] = 0xff & div;
+fail:
+ return ret;
+}
+
+/*---------------------------------------------------------------------*/
+
+int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets bp filter bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, BP_FILTER, freq, &val);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_EP1] &= ~0x07; /* clear bp filter bits */
+ regs[R_EP1] |= (0x07 & val);
+fail:
+ return ret;
+}
+
+int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets K & M bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, RF_CAL_KMCO, freq, &val);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_EB13] &= ~0x7c; /* clear k & m bits */
+ regs[R_EB13] |= (0x7c & val);
+fail:
+ return ret;
+}
+
+int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets rf band bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, RF_BAND, freq, &val);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_EP2] &= ~0xe0; /* clear rf band bits */
+ regs[R_EP2] |= (0xe0 & (val << 5));
+fail:
+ return ret;
+}
+
+int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets gain taper bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, GAIN_TAPER, freq, &val);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_EP2] &= ~0x1f; /* clear gain taper bits */
+ regs[R_EP2] |= (0x1f & val);
+fail:
+ return ret;
+}
+
+int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets IR Meas bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, IR_MEASURE, freq, &val);
+ if (ret < 0)
+ goto fail;
+
+ regs[R_EP5] &= ~0x07;
+ regs[R_EP5] |= (0x07 & val);
+fail:
+ return ret;
+}
+
+int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets rf cal byte (RFC_Cprog), but does not write it */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ tda18271_lookup_map(fe, RF_CAL, freq, &val);
+
+ regs[R_EB14] = val;
+
+ return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/tda18271-fe.c b/drivers/media/dvb/frontends/tda18271-fe.c
new file mode 100644
index 000000000000..dfe72aaec380
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda18271-fe.c
@@ -0,0 +1,1225 @@
+/*
+ tda18271-fe.c - driver for the Philips / NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include "tda18271-priv.h"
+
+int tda18271_debug;
+module_param_named(debug, tda18271_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debug level "
+ "(info=1, map=2, reg=4, adv=8, cal=16 (or-able))");
+
+static int tda18271_cal_on_startup;
+module_param_named(cal, tda18271_cal_on_startup, int, 0644);
+MODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup");
+
+static LIST_HEAD(tda18271_list);
+static DEFINE_MUTEX(tda18271_list_mutex);
+
+/*---------------------------------------------------------------------*/
+
+static int tda18271_ir_cal_init(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ tda18271_read_regs(fe);
+
+ /* test IR_CAL_OK to see if we need init */
+ if ((regs[R_EP1] & 0x08) == 0)
+ tda18271_init_regs(fe);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271_channel_configuration(struct dvb_frontend *fe,
+ u32 ifc, u32 freq, u32 bw, u8 std,
+ int radio)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u32 N;
+
+ /* update TV broadcast parameters */
+
+ /* set standard */
+ regs[R_EP3] &= ~0x1f; /* clear std bits */
+ regs[R_EP3] |= std;
+
+ /* set cal mode to normal */
+ regs[R_EP4] &= ~0x03;
+
+ /* update IF output level & IF notch frequency */
+ regs[R_EP4] &= ~0x1c; /* clear if level bits */
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ regs[R_MPD] &= ~0x80; /* IF notch = 0 */
+ break;
+ case TDA18271_DIGITAL:
+ regs[R_EP4] |= 0x04; /* IF level = 1 */
+ regs[R_MPD] |= 0x80; /* IF notch = 1 */
+ break;
+ }
+
+ if (radio)
+ regs[R_EP4] |= 0x80;
+ else
+ regs[R_EP4] &= ~0x80;
+
+ /* update RF_TOP / IF_TOP */
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ regs[R_EB22] = 0x2c;
+ break;
+ case TDA18271_DIGITAL:
+ regs[R_EB22] = 0x37;
+ break;
+ }
+ tda18271_write_regs(fe, R_EB22, 1);
+
+ /* --------------------------------------------------------------- */
+
+ /* disable Power Level Indicator */
+ regs[R_EP1] |= 0x40;
+
+ /* frequency dependent parameters */
+
+ tda18271_calc_ir_measure(fe, &freq);
+
+ tda18271_calc_bp_filter(fe, &freq);
+
+ tda18271_calc_rf_band(fe, &freq);
+
+ tda18271_calc_gain_taper(fe, &freq);
+
+ /* --------------------------------------------------------------- */
+
+ /* dual tuner and agc1 extra configuration */
+
+ /* main vco when Master, cal vco when slave */
+ regs[R_EB1] |= 0x04; /* FIXME: assumes master */
+
+ /* agc1 always active */
+ regs[R_EB1] &= ~0x02;
+
+ /* agc1 has priority on agc2 */
+ regs[R_EB1] &= ~0x01;
+
+ tda18271_write_regs(fe, R_EB1, 1);
+
+ /* --------------------------------------------------------------- */
+
+ N = freq + ifc;
+
+ /* FIXME: assumes master */
+ tda18271_calc_main_pll(fe, N);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ tda18271_write_regs(fe, R_TM, 7);
+
+ /* main pll charge pump source */
+ regs[R_EB4] |= 0x20;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ msleep(1);
+
+ /* normal operation for the main pll */
+ regs[R_EB4] &= ~0x20;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ msleep(5);
+
+ return 0;
+}
+
+static int tda18271_read_thermometer(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int tm;
+
+ /* switch thermometer on */
+ regs[R_TM] |= 0x10;
+ tda18271_write_regs(fe, R_TM, 1);
+
+ /* read thermometer info */
+ tda18271_read_regs(fe);
+
+ if ((((regs[R_TM] & 0x0f) == 0x00) && ((regs[R_TM] & 0x20) == 0x20)) ||
+ (((regs[R_TM] & 0x0f) == 0x08) && ((regs[R_TM] & 0x20) == 0x00))) {
+
+ if ((regs[R_TM] & 0x20) == 0x20)
+ regs[R_TM] &= ~0x20;
+ else
+ regs[R_TM] |= 0x20;
+
+ tda18271_write_regs(fe, R_TM, 1);
+
+ msleep(10); /* temperature sensing */
+
+ /* read thermometer info */
+ tda18271_read_regs(fe);
+ }
+
+ tm = tda18271_lookup_thermometer(fe);
+
+ /* switch thermometer off */
+ regs[R_TM] &= ~0x10;
+ tda18271_write_regs(fe, R_TM, 1);
+
+ /* set CAL mode to normal */
+ regs[R_EP4] &= ~0x03;
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ return tm;
+}
+
+static int tda18271_rf_tracking_filters_correction(struct dvb_frontend *fe,
+ u32 freq)
+{
+ 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;
+ u8 dc_over_dt, rf_tab;
+
+ /* power up */
+ tda18271_set_standby_mode(fe, 0, 0, 0);
+
+ /* read die current temperature */
+ tm_current = tda18271_read_thermometer(fe);
+
+ /* frequency dependent parameters */
+
+ tda18271_calc_rf_cal(fe, &freq);
+ rf_tab = regs[R_EB14];
+
+ i = tda18271_lookup_rf_band(fe, &freq, NULL);
+ if (i < 0)
+ return -EINVAL;
+
+ if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) {
+ approx = map[i].rf_a1 *
+ (freq / 1000 - map[i].rf1) + map[i].rf_b1 + rf_tab;
+ } else {
+ approx = map[i].rf_a2 *
+ (freq / 1000 - map[i].rf2) + map[i].rf_b2 + rf_tab;
+ }
+
+ if (approx < 0)
+ approx = 0;
+ if (approx > 255)
+ approx = 255;
+
+ tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt);
+
+ /* calculate temperature compensation */
+ 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;
+}
+
+static int tda18271_por(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ /* power up detector 1 */
+ regs[R_EB12] &= ~0x20;
+ tda18271_write_regs(fe, R_EB12, 1);
+
+ 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);
+
+ regs[R_EB21] |= 0x03; /* set agc2_gain to -6 dB */
+
+ /* POR mode */
+ tda18271_set_standby_mode(fe, 1, 0, 0);
+
+ /* 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;
+}
+
+static int tda18271_calibrate_rf(struct dvb_frontend *fe, u32 freq)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u32 N;
+
+ /* set CAL mode to normal */
+ regs[R_EP4] &= ~0x03;
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ /* switch off agc1 */
+ regs[R_EP3] |= 0x40; /* sm_lt = 1 */
+
+ regs[R_EB18] |= 0x03; /* set agc1_gain to 15 dB */
+ tda18271_write_regs(fe, R_EB18, 1);
+
+ /* frequency dependent parameters */
+
+ tda18271_calc_bp_filter(fe, &freq);
+ tda18271_calc_gain_taper(fe, &freq);
+ tda18271_calc_rf_band(fe, &freq);
+ tda18271_calc_km(fe, &freq);
+
+ tda18271_write_regs(fe, R_EP1, 3);
+ tda18271_write_regs(fe, R_EB13, 1);
+
+ /* main pll charge pump source */
+ regs[R_EB4] |= 0x20;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ /* cal pll charge pump source */
+ regs[R_EB7] |= 0x20;
+ tda18271_write_regs(fe, R_EB7, 1);
+
+ /* force dcdc converter to 0 V */
+ regs[R_EB14] = 0x00;
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ /* disable plls lock */
+ regs[R_EB20] &= ~0x20;
+ tda18271_write_regs(fe, R_EB20, 1);
+
+ /* set CAL mode to RF tracking filter calibration */
+ regs[R_EP4] |= 0x03;
+ tda18271_write_regs(fe, R_EP4, 2);
+
+ /* --------------------------------------------------------------- */
+
+ /* set the internal calibration signal */
+ N = freq;
+
+ tda18271_calc_main_pll(fe, N);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ /* downconvert internal calibration */
+ N += 1000000;
+
+ tda18271_calc_main_pll(fe, N);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ msleep(5);
+
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EP1, 1);
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ /* --------------------------------------------------------------- */
+
+ /* normal operation for the main pll */
+ regs[R_EB4] &= ~0x20;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ /* normal operation for the cal pll */
+ regs[R_EB7] &= ~0x20;
+ tda18271_write_regs(fe, R_EB7, 1);
+
+ msleep(5); /* plls locking */
+
+ /* launch the rf tracking filters calibration */
+ regs[R_EB20] |= 0x20;
+ tda18271_write_regs(fe, R_EB20, 1);
+
+ msleep(60); /* calibration */
+
+ /* --------------------------------------------------------------- */
+
+ /* set CAL mode to normal */
+ regs[R_EP4] &= ~0x03;
+
+ /* switch on agc1 */
+ regs[R_EP3] &= ~0x40; /* sm_lt = 0 */
+
+ regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */
+ tda18271_write_regs(fe, R_EB18, 1);
+
+ tda18271_write_regs(fe, R_EP3, 2);
+
+ /* synchronization */
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ /* get calibration result */
+ tda18271_read_extended(fe);
+
+ return regs[R_EB14];
+}
+
+static int tda18271_powerscan(struct dvb_frontend *fe,
+ u32 *freq_in, u32 *freq_out)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int sgn, bcal, count, wait;
+ u8 cid_target;
+ u16 count_limit;
+ u32 freq;
+
+ freq = *freq_in;
+
+ tda18271_calc_rf_band(fe, &freq);
+ tda18271_calc_rf_cal(fe, &freq);
+ tda18271_calc_gain_taper(fe, &freq);
+ tda18271_lookup_cid_target(fe, &freq, &cid_target, &count_limit);
+
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ /* downconvert frequency */
+ freq += 1000000;
+
+ tda18271_calc_main_pll(fe, freq);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ msleep(5); /* pll locking */
+
+ /* detection mode */
+ regs[R_EP4] &= ~0x03;
+ regs[R_EP4] |= 0x01;
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ /* launch power detection measurement */
+ tda18271_write_regs(fe, R_EP2, 1);
+
+ /* read power detection info, stored in EB10 */
+ tda18271_read_extended(fe);
+
+ /* algorithm initialization */
+ sgn = 1;
+ *freq_out = *freq_in;
+ bcal = 0;
+ count = 0;
+ wait = false;
+
+ while ((regs[R_EB10] & 0x3f) < cid_target) {
+ /* downconvert updated freq to 1 MHz */
+ freq = *freq_in + (sgn * count) + 1000000;
+
+ tda18271_calc_main_pll(fe, freq);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ if (wait) {
+ msleep(5); /* pll locking */
+ wait = false;
+ } else
+ udelay(100); /* pll locking */
+
+ /* launch power detection measurement */
+ tda18271_write_regs(fe, R_EP2, 1);
+
+ /* read power detection info, stored in EB10 */
+ tda18271_read_extended(fe);
+
+ count += 200;
+
+ if (count < count_limit)
+ continue;
+
+ if (sgn <= 0)
+ break;
+
+ sgn = -1 * sgn;
+ count = 200;
+ wait = true;
+ }
+
+ if ((regs[R_EB10] & 0x3f) >= cid_target) {
+ bcal = 1;
+ *freq_out = freq - 1000000;
+ } else
+ bcal = 0;
+
+ tda_cal("bcal = %d, freq_in = %d, freq_out = %d (freq = %d)\n",
+ bcal, *freq_in, *freq_out, freq);
+
+ return bcal;
+}
+
+static int tda18271_powerscan_init(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ /* set standard to digital */
+ regs[R_EP3] &= ~0x1f; /* clear std bits */
+ regs[R_EP3] |= 0x12;
+
+ /* set cal mode to normal */
+ regs[R_EP4] &= ~0x03;
+
+ /* update IF output level & IF notch frequency */
+ regs[R_EP4] &= ~0x1c; /* clear if level bits */
+
+ tda18271_write_regs(fe, R_EP3, 2);
+
+ regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */
+ tda18271_write_regs(fe, R_EB18, 1);
+
+ regs[R_EB21] &= ~0x03; /* set agc2_gain to -15 dB */
+
+ /* 1.5 MHz low pass filter */
+ 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;
+}
+
+static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq)
+{
+ 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 bcal, rf, i;
+#define RF1 0
+#define RF2 1
+#define RF3 2
+ u32 rf_default[3];
+ u32 rf_freq[3];
+ u8 prog_cal[3];
+ u8 prog_tab[3];
+
+ i = tda18271_lookup_rf_band(fe, &freq, NULL);
+
+ if (i < 0)
+ return i;
+
+ rf_default[RF1] = 1000 * map[i].rf1_def;
+ rf_default[RF2] = 1000 * map[i].rf2_def;
+ rf_default[RF3] = 1000 * map[i].rf3_def;
+
+ for (rf = RF1; rf <= RF3; rf++) {
+ if (0 == rf_default[rf])
+ return 0;
+ tda_cal("freq = %d, rf = %d\n", freq, rf);
+
+ /* look for optimized calibration frequency */
+ bcal = tda18271_powerscan(fe, &rf_default[rf], &rf_freq[rf]);
+
+ tda18271_calc_rf_cal(fe, &rf_freq[rf]);
+ prog_tab[rf] = regs[R_EB14];
+
+ if (1 == bcal)
+ prog_cal[rf] = tda18271_calibrate_rf(fe, rf_freq[rf]);
+ else
+ prog_cal[rf] = prog_tab[rf];
+
+ switch (rf) {
+ case RF1:
+ map[i].rf_a1 = 0;
+ map[i].rf_b1 = prog_cal[RF1] - prog_tab[RF1];
+ map[i].rf1 = rf_freq[RF1] / 1000;
+ break;
+ case RF2:
+ map[i].rf_a1 = (prog_cal[RF2] - prog_tab[RF2] -
+ prog_cal[RF1] + prog_tab[RF1]) /
+ ((rf_freq[RF2] - rf_freq[RF1]) / 1000);
+ map[i].rf2 = rf_freq[RF2] / 1000;
+ break;
+ case RF3:
+ map[i].rf_a2 = (prog_cal[RF3] - prog_tab[RF3] -
+ prog_cal[RF2] + prog_tab[RF2]) /
+ ((rf_freq[RF3] - rf_freq[RF2]) / 1000);
+ map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2];
+ map[i].rf3 = rf_freq[RF3] / 1000;
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ return 0;
+}
+
+static int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned int i;
+
+ tda_info("tda18271: performing RF tracking filter calibration\n");
+
+ /* wait for die temperature stabilization */
+ msleep(200);
+
+ tda18271_powerscan_init(fe);
+
+ /* rf band calibration */
+ for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++)
+ tda18271_rf_tracking_filters_init(fe, 1000 *
+ priv->rf_cal_state[i].rfmax);
+
+ priv->tm_rfcal = tda18271_read_thermometer(fe);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271_rf_cal_init(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ /* test RF_CAL_OK to see if we need init */
+ if ((regs[R_EP1] & 0x10) == 0)
+ priv->cal_initialized = false;
+
+ if (priv->cal_initialized)
+ return 0;
+
+ tda18271_calc_rf_filter_curve(fe);
+
+ tda18271_por(fe);
+
+ tda_info("tda18271: RF tracking filter calibration complete\n");
+
+ priv->cal_initialized = true;
+
+ return 0;
+}
+
+static int tda18271_init(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ mutex_lock(&priv->lock);
+
+ /* power up */
+ tda18271_set_standby_mode(fe, 0, 0, 0);
+
+ /* initialization */
+ tda18271_ir_cal_init(fe);
+
+ if (priv->id == TDA18271HDC2)
+ tda18271_rf_cal_init(fe);
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int tda18271c2_tune(struct dvb_frontend *fe,
+ u32 ifc, u32 freq, u32 bw, u8 std, int radio)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ tda_dbg("freq = %d, ifc = %d\n", freq, ifc);
+
+ tda18271_init(fe);
+
+ mutex_lock(&priv->lock);
+
+ tda18271_rf_tracking_filters_correction(fe, freq);
+
+ tda18271_channel_configuration(fe, ifc, freq, bw, std, radio);
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271c1_tune(struct dvb_frontend *fe,
+ u32 ifc, u32 freq, u32 bw, u8 std, int radio)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u32 N = 0;
+
+ tda18271_init(fe);
+
+ mutex_lock(&priv->lock);
+
+ tda_dbg("freq = %d, ifc = %d\n", freq, ifc);
+
+ /* RF tracking filter calibration */
+
+ /* calculate bp filter */
+ tda18271_calc_bp_filter(fe, &freq);
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ regs[R_EB4] &= 0x07;
+ regs[R_EB4] |= 0x60;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ regs[R_EB7] = 0x60;
+ tda18271_write_regs(fe, R_EB7, 1);
+
+ regs[R_EB14] = 0x00;
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ regs[R_EB20] = 0xcc;
+ tda18271_write_regs(fe, R_EB20, 1);
+
+ /* set cal mode to RF tracking filter calibration */
+ regs[R_EP4] |= 0x03;
+
+ /* calculate cal pll */
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ N = freq - 1250000;
+ break;
+ case TDA18271_DIGITAL:
+ N = freq + bw / 2;
+ break;
+ }
+
+ tda18271_calc_cal_pll(fe, N);
+
+ /* calculate main pll */
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ N = freq - 250000;
+ break;
+ case TDA18271_DIGITAL:
+ N = freq + bw / 2 + 1000000;
+ break;
+ }
+
+ tda18271_calc_main_pll(fe, N);
+
+ tda18271_write_regs(fe, R_EP3, 11);
+ msleep(5); /* RF tracking filter calibration initialization */
+
+ /* search for K,M,CO for RF calibration */
+ tda18271_calc_km(fe, &freq);
+ tda18271_write_regs(fe, R_EB13, 1);
+
+ /* search for rf band */
+ tda18271_calc_rf_band(fe, &freq);
+
+ /* search for gain taper */
+ tda18271_calc_gain_taper(fe, &freq);
+
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EP1, 1);
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ regs[R_EB4] &= 0x07;
+ regs[R_EB4] |= 0x40;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ regs[R_EB7] = 0x40;
+ tda18271_write_regs(fe, R_EB7, 1);
+ msleep(10);
+
+ regs[R_EB20] = 0xec;
+ tda18271_write_regs(fe, R_EB20, 1);
+ msleep(60); /* RF tracking filter calibration completion */
+
+ regs[R_EP4] &= ~0x03; /* set cal mode to normal */
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ /* RF tracking filter correction for VHF_Low band */
+ if (0 == tda18271_calc_rf_cal(fe, &freq))
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ /* Channel Configuration */
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ regs[R_EB22] = 0x2c;
+ break;
+ case TDA18271_DIGITAL:
+ regs[R_EB22] = 0x37;
+ break;
+ }
+ tda18271_write_regs(fe, R_EB22, 1);
+
+ regs[R_EP1] |= 0x40; /* set dis power level on */
+
+ /* set standard */
+ regs[R_EP3] &= ~0x1f; /* clear std bits */
+
+ /* see table 22 */
+ regs[R_EP3] |= std;
+
+ regs[R_EP4] &= ~0x03; /* set cal mode to normal */
+
+ regs[R_EP4] &= ~0x1c; /* clear if level bits */
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ regs[R_MPD] &= ~0x80; /* IF notch = 0 */
+ break;
+ case TDA18271_DIGITAL:
+ regs[R_EP4] |= 0x04;
+ regs[R_MPD] |= 0x80;
+ break;
+ }
+
+ if (radio)
+ regs[R_EP4] |= 0x80;
+ else
+ regs[R_EP4] &= ~0x80;
+
+ /* image rejection validity */
+ tda18271_calc_ir_measure(fe, &freq);
+
+ /* calculate MAIN PLL */
+ N = freq + ifc;
+
+ tda18271_calc_main_pll(fe, N);
+
+ tda18271_write_regs(fe, R_TM, 15);
+ msleep(5);
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static inline int tda18271_tune(struct dvb_frontend *fe,
+ u32 ifc, u32 freq, u32 bw, u8 std, int radio)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ int ret = -EINVAL;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ ret = tda18271c1_tune(fe, ifc, freq, bw, std, radio);
+ break;
+ case TDA18271HDC2:
+ ret = tda18271c2_tune(fe, ifc, freq, bw, std, radio);
+ break;
+ }
+ return ret;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_std_map *std_map = &priv->std;
+ int ret;
+ u8 std;
+ u16 sgIF;
+ u32 bw, freq = params->frequency;
+
+ priv->mode = TDA18271_DIGITAL;
+
+ if (fe->ops.info.type == FE_ATSC) {
+ switch (params->u.vsb.modulation) {
+ case VSB_8:
+ case VSB_16:
+ std = std_map->atsc_6.std_bits;
+ sgIF = std_map->atsc_6.if_freq;
+ break;
+ case QAM_64:
+ case QAM_256:
+ std = std_map->qam_6.std_bits;
+ sgIF = std_map->qam_6.if_freq;
+ break;
+ default:
+ tda_warn("modulation not set!\n");
+ return -EINVAL;
+ }
+#if 0
+ /* userspace request is already center adjusted */
+ freq += 1750000; /* Adjust to center (+1.75MHZ) */
+#endif
+ bw = 6000000;
+ } else if (fe->ops.info.type == FE_OFDM) {
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ bw = 6000000;
+ std = std_map->dvbt_6.std_bits;
+ sgIF = std_map->dvbt_6.if_freq;
+ break;
+ case BANDWIDTH_7_MHZ:
+ bw = 7000000;
+ std = std_map->dvbt_7.std_bits;
+ sgIF = std_map->dvbt_7.if_freq;
+ break;
+ case BANDWIDTH_8_MHZ:
+ bw = 8000000;
+ std = std_map->dvbt_8.std_bits;
+ sgIF = std_map->dvbt_8.if_freq;
+ break;
+ default:
+ tda_warn("bandwidth not set!\n");
+ return -EINVAL;
+ }
+ } else {
+ tda_warn("modulation type not supported!\n");
+ return -EINVAL;
+ }
+
+ /* When tuning digital, the analog demod must be tri-stated */
+ if (fe->ops.analog_ops.standby)
+ fe->ops.analog_ops.standby(fe);
+
+ ret = tda18271_tune(fe, sgIF * 1000, freq, bw, std, 0);
+
+ if (ret < 0)
+ goto fail;
+
+ priv->frequency = freq;
+ priv->bandwidth = (fe->ops.info.type == FE_OFDM) ?
+ params->u.ofdm.bandwidth : 0;
+fail:
+ return ret;
+}
+
+static int tda18271_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_std_map *std_map = &priv->std;
+ char *mode;
+ int ret, radio = 0;
+ u8 std;
+ u16 sgIF;
+ u32 freq = params->frequency * 62500;
+
+ priv->mode = TDA18271_ANALOG;
+
+ if (params->mode == V4L2_TUNER_RADIO) {
+ radio = 1;
+ freq = freq / 1000;
+ std = std_map->fm_radio.std_bits;
+ sgIF = std_map->fm_radio.if_freq;
+ mode = "fm";
+ } else if (params->std & V4L2_STD_MN) {
+ std = std_map->atv_mn.std_bits;
+ sgIF = std_map->atv_mn.if_freq;
+ mode = "MN";
+ } else if (params->std & V4L2_STD_B) {
+ std = std_map->atv_b.std_bits;
+ sgIF = std_map->atv_b.if_freq;
+ mode = "B";
+ } else if (params->std & V4L2_STD_GH) {
+ std = std_map->atv_gh.std_bits;
+ sgIF = std_map->atv_gh.if_freq;
+ mode = "GH";
+ } else if (params->std & V4L2_STD_PAL_I) {
+ std = std_map->atv_i.std_bits;
+ sgIF = std_map->atv_i.if_freq;
+ mode = "I";
+ } else if (params->std & V4L2_STD_DK) {
+ std = std_map->atv_dk.std_bits;
+ sgIF = std_map->atv_dk.if_freq;
+ mode = "DK";
+ } else if (params->std & V4L2_STD_SECAM_L) {
+ std = std_map->atv_l.std_bits;
+ sgIF = std_map->atv_l.if_freq;
+ mode = "L";
+ } else if (params->std & V4L2_STD_SECAM_LC) {
+ std = std_map->atv_lc.std_bits;
+ sgIF = std_map->atv_lc.if_freq;
+ mode = "L'";
+ } else {
+ std = std_map->atv_i.std_bits;
+ sgIF = std_map->atv_i.if_freq;
+ mode = "xx";
+ }
+
+ tda_dbg("setting tda18271 to system %s\n", mode);
+
+ ret = tda18271_tune(fe, sgIF * 1000, freq, 0, std, radio);
+
+ if (ret < 0)
+ goto fail;
+
+ priv->frequency = freq;
+ priv->bandwidth = 0;
+fail:
+ return ret;
+}
+
+static int tda18271_sleep(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ mutex_lock(&priv->lock);
+
+ /* standby mode w/ slave tuner output
+ * & loop thru & xtal oscillator on */
+ tda18271_set_standby_mode(fe, 1, 0, 0);
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int tda18271_release(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ mutex_lock(&tda18271_list_mutex);
+
+ priv->count--;
+
+ if (!priv->count) {
+ tda_dbg("destroying instance @ %d-%04x\n",
+ i2c_adapter_id(priv->i2c_adap),
+ priv->i2c_addr);
+ list_del(&priv->tda18271_list);
+
+ kfree(priv);
+ }
+ mutex_unlock(&tda18271_list_mutex);
+
+ fe->tuner_priv = NULL;
+
+ return 0;
+}
+
+static int tda18271_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int tda18271_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+#define tda18271_update_std(std_cfg, name) do { \
+ if (map->std_cfg.if_freq + map->std_cfg.std_bits > 0) { \
+ tda_dbg("Using custom std config for %s\n", name); \
+ memcpy(&std->std_cfg, &map->std_cfg, \
+ sizeof(struct tda18271_std_map_item)); \
+ } } while (0)
+
+#define tda18271_dump_std_item(std_cfg, name) do { \
+ tda_dbg("(%s) if freq = %d, std bits = 0x%02x\n", \
+ name, std->std_cfg.if_freq, std->std_cfg.std_bits); \
+ } while (0)
+
+static int tda18271_dump_std_map(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_std_map *std = &priv->std;
+
+ tda_dbg("========== STANDARD MAP SETTINGS ==========\n");
+ tda18271_dump_std_item(fm_radio, "fm");
+ tda18271_dump_std_item(atv_b, "pal b");
+ tda18271_dump_std_item(atv_dk, "pal dk");
+ tda18271_dump_std_item(atv_gh, "pal gh");
+ tda18271_dump_std_item(atv_i, "pal i");
+ tda18271_dump_std_item(atv_l, "pal l");
+ tda18271_dump_std_item(atv_lc, "pal l'");
+ tda18271_dump_std_item(atv_mn, "atv mn");
+ tda18271_dump_std_item(atsc_6, "atsc 6");
+ tda18271_dump_std_item(dvbt_6, "dvbt 6");
+ tda18271_dump_std_item(dvbt_7, "dvbt 7");
+ tda18271_dump_std_item(dvbt_8, "dvbt 8");
+ tda18271_dump_std_item(qam_6, "qam 6");
+ tda18271_dump_std_item(qam_8, "qam 8");
+
+ return 0;
+}
+
+static int tda18271_update_std_map(struct dvb_frontend *fe,
+ struct tda18271_std_map *map)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_std_map *std = &priv->std;
+
+ if (!map)
+ return -EINVAL;
+
+ tda18271_update_std(fm_radio, "fm");
+ tda18271_update_std(atv_b, "atv b");
+ tda18271_update_std(atv_dk, "atv dk");
+ tda18271_update_std(atv_gh, "atv gh");
+ tda18271_update_std(atv_i, "atv i");
+ tda18271_update_std(atv_l, "atv l");
+ tda18271_update_std(atv_lc, "atv l'");
+ tda18271_update_std(atv_mn, "atv mn");
+ tda18271_update_std(atsc_6, "atsc 6");
+ tda18271_update_std(dvbt_6, "dvbt 6");
+ tda18271_update_std(dvbt_7, "dvbt 7");
+ tda18271_update_std(dvbt_8, "dvbt 8");
+ tda18271_update_std(qam_6, "qam 6");
+ tda18271_update_std(qam_8, "qam 8");
+
+ return 0;
+}
+
+static int tda18271_get_id(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ char *name;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+ tda18271_read_regs(fe);
+ mutex_unlock(&priv->lock);
+
+ switch (regs[R_ID] & 0x7f) {
+ case 3:
+ name = "TDA18271HD/C1";
+ priv->id = TDA18271HDC1;
+ break;
+ case 4:
+ name = "TDA18271HD/C2";
+ priv->id = TDA18271HDC2;
+ break;
+ default:
+ name = "Unknown device";
+ ret = -EINVAL;
+ break;
+ }
+
+ tda_info("%s detected @ %d-%04x%s\n", name,
+ i2c_adapter_id(priv->i2c_adap), priv->i2c_addr,
+ (0 == ret) ? "" : ", device not supported.");
+
+ return ret;
+}
+
+static struct dvb_tuner_ops tda18271_tuner_ops = {
+ .info = {
+ .name = "NXP TDA18271HD",
+ .frequency_min = 45000000,
+ .frequency_max = 864000000,
+ .frequency_step = 62500
+ },
+ .init = tda18271_init,
+ .sleep = tda18271_sleep,
+ .set_params = tda18271_set_params,
+ .set_analog_params = tda18271_set_analog_params,
+ .release = tda18271_release,
+ .get_frequency = tda18271_get_frequency,
+ .get_bandwidth = tda18271_get_bandwidth,
+};
+
+struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
+ struct i2c_adapter *i2c,
+ struct tda18271_config *cfg)
+{
+ struct tda18271_priv *priv = NULL;
+ int state_found = 0;
+
+ mutex_lock(&tda18271_list_mutex);
+
+ list_for_each_entry(priv, &tda18271_list, tda18271_list) {
+ if ((i2c_adapter_id(priv->i2c_adap) == i2c_adapter_id(i2c)) &&
+ (priv->i2c_addr == addr)) {
+ tda_dbg("attaching existing tuner @ %d-%04x\n",
+ i2c_adapter_id(priv->i2c_adap),
+ priv->i2c_addr);
+ priv->count++;
+ fe->tuner_priv = priv;
+ state_found = 1;
+ /* allow dvb driver to override i2c gate setting */
+ if ((cfg) && (cfg->gate != TDA18271_GATE_ANALOG))
+ priv->gate = cfg->gate;
+ break;
+ }
+ }
+ if (state_found == 0) {
+ tda_dbg("creating new tuner instance @ %d-%04x\n",
+ i2c_adapter_id(i2c), addr);
+
+ priv = kzalloc(sizeof(struct tda18271_priv), GFP_KERNEL);
+ if (priv == NULL) {
+ mutex_unlock(&tda18271_list_mutex);
+ return NULL;
+ }
+
+ priv->i2c_addr = addr;
+ priv->i2c_adap = i2c;
+ priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
+ priv->cal_initialized = false;
+ mutex_init(&priv->lock);
+ priv->count++;
+
+ fe->tuner_priv = priv;
+
+ list_add_tail(&priv->tda18271_list, &tda18271_list);
+
+ if (tda18271_get_id(fe) < 0)
+ goto fail;
+
+ if (tda18271_assign_map_layout(fe) < 0)
+ goto fail;
+
+ mutex_lock(&priv->lock);
+ tda18271_init_regs(fe);
+
+ if ((tda18271_cal_on_startup) && (priv->id == TDA18271HDC2))
+ tda18271_rf_cal_init(fe);
+
+ mutex_unlock(&priv->lock);
+ }
+
+ /* override default std map with values in config struct */
+ if ((cfg) && (cfg->std_map))
+ tda18271_update_std_map(fe, cfg->std_map);
+
+ mutex_unlock(&tda18271_list_mutex);
+
+ memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ if (tda18271_debug & DBG_MAP)
+ tda18271_dump_std_map(fe);
+
+ return fe;
+fail:
+ mutex_unlock(&tda18271_list_mutex);
+
+ tda18271_release(fe);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(tda18271_attach);
+MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver");
+MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.2");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/tda18271-priv.h b/drivers/media/dvb/frontends/tda18271-priv.h
new file mode 100644
index 000000000000..7b939a5325fb
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda18271-priv.h
@@ -0,0 +1,212 @@
+/*
+ tda18271-priv.h - private header for the NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, 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 __TDA18271_PRIV_H__
+#define __TDA18271_PRIV_H__
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include "tda18271.h"
+
+#define R_ID 0x00 /* ID byte */
+#define R_TM 0x01 /* Thermo byte */
+#define R_PL 0x02 /* Power level byte */
+#define R_EP1 0x03 /* Easy Prog byte 1 */
+#define R_EP2 0x04 /* Easy Prog byte 2 */
+#define R_EP3 0x05 /* Easy Prog byte 3 */
+#define R_EP4 0x06 /* Easy Prog byte 4 */
+#define R_EP5 0x07 /* Easy Prog byte 5 */
+#define R_CPD 0x08 /* Cal Post-Divider byte */
+#define R_CD1 0x09 /* Cal Divider byte 1 */
+#define R_CD2 0x0a /* Cal Divider byte 2 */
+#define R_CD3 0x0b /* Cal Divider byte 3 */
+#define R_MPD 0x0c /* Main Post-Divider byte */
+#define R_MD1 0x0d /* Main Divider byte 1 */
+#define R_MD2 0x0e /* Main Divider byte 2 */
+#define R_MD3 0x0f /* Main Divider byte 3 */
+#define R_EB1 0x10 /* Extended byte 1 */
+#define R_EB2 0x11 /* Extended byte 2 */
+#define R_EB3 0x12 /* Extended byte 3 */
+#define R_EB4 0x13 /* Extended byte 4 */
+#define R_EB5 0x14 /* Extended byte 5 */
+#define R_EB6 0x15 /* Extended byte 6 */
+#define R_EB7 0x16 /* Extended byte 7 */
+#define R_EB8 0x17 /* Extended byte 8 */
+#define R_EB9 0x18 /* Extended byte 9 */
+#define R_EB10 0x19 /* Extended byte 10 */
+#define R_EB11 0x1a /* Extended byte 11 */
+#define R_EB12 0x1b /* Extended byte 12 */
+#define R_EB13 0x1c /* Extended byte 13 */
+#define R_EB14 0x1d /* Extended byte 14 */
+#define R_EB15 0x1e /* Extended byte 15 */
+#define R_EB16 0x1f /* Extended byte 16 */
+#define R_EB17 0x20 /* Extended byte 17 */
+#define R_EB18 0x21 /* Extended byte 18 */
+#define R_EB19 0x22 /* Extended byte 19 */
+#define R_EB20 0x23 /* Extended byte 20 */
+#define R_EB21 0x24 /* Extended byte 21 */
+#define R_EB22 0x25 /* Extended byte 22 */
+#define R_EB23 0x26 /* Extended byte 23 */
+
+#define TDA18271_NUM_REGS 39
+
+/*---------------------------------------------------------------------*/
+
+struct tda18271_rf_tracking_filter_cal {
+ u32 rfmax;
+ u8 rfband;
+ u32 rf1_def;
+ u32 rf2_def;
+ u32 rf3_def;
+ u32 rf1;
+ u32 rf2;
+ u32 rf3;
+ int rf_a1;
+ int rf_b1;
+ int rf_a2;
+ int rf_b2;
+};
+
+enum tda18271_mode {
+ TDA18271_ANALOG,
+ TDA18271_DIGITAL,
+};
+
+struct tda18271_map_layout;
+
+enum tda18271_ver {
+ TDA18271HDC1,
+ TDA18271HDC2,
+};
+
+struct tda18271_priv {
+ u8 i2c_addr;
+ struct i2c_adapter *i2c_adap;
+ unsigned char tda18271_regs[TDA18271_NUM_REGS];
+
+ struct list_head tda18271_list;
+
+ enum tda18271_mode mode;
+ enum tda18271_i2c_gate gate;
+ enum tda18271_ver id;
+
+ unsigned int count;
+ unsigned int tm_rfcal;
+ unsigned int cal_initialized:1;
+
+ struct tda18271_map_layout *maps;
+ struct tda18271_std_map std;
+ struct tda18271_rf_tracking_filter_cal rf_cal_state[8];
+
+ struct mutex lock;
+
+ u32 frequency;
+ u32 bandwidth;
+};
+
+/*---------------------------------------------------------------------*/
+
+extern int tda18271_debug;
+
+#define DBG_INFO 1
+#define DBG_MAP 2
+#define DBG_REG 4
+#define DBG_ADV 8
+#define DBG_CAL 16
+
+#define tda_printk(kern, fmt, arg...) \
+ printk(kern "%s: " fmt, __FUNCTION__, ##arg)
+
+#define dprintk(kern, lvl, fmt, arg...) do {\
+ if (tda18271_debug & lvl) \
+ tda_printk(kern, fmt, ##arg); } while (0)
+
+#define tda_info(fmt, arg...) printk(KERN_INFO fmt, ##arg)
+#define tda_warn(fmt, arg...) tda_printk(KERN_WARNING, fmt, ##arg)
+#define tda_err(fmt, arg...) tda_printk(KERN_ERR, fmt, ##arg)
+#define tda_dbg(fmt, arg...) dprintk(KERN_DEBUG, DBG_INFO, fmt, ##arg)
+#define tda_map(fmt, arg...) dprintk(KERN_DEBUG, DBG_MAP, fmt, ##arg)
+#define tda_reg(fmt, arg...) dprintk(KERN_DEBUG, DBG_REG, fmt, ##arg)
+#define tda_cal(fmt, arg...) dprintk(KERN_DEBUG, DBG_CAL, fmt, ##arg)
+
+/*---------------------------------------------------------------------*/
+
+enum tda18271_map_type {
+ /* tda18271_pll_map */
+ MAIN_PLL,
+ CAL_PLL,
+ /* tda18271_map */
+ RF_CAL,
+ RF_CAL_KMCO,
+ RF_CAL_DC_OVER_DT,
+ BP_FILTER,
+ RF_BAND,
+ GAIN_TAPER,
+ IR_MEASURE,
+};
+
+extern int tda18271_lookup_pll_map(struct dvb_frontend *fe,
+ enum tda18271_map_type map_type,
+ u32 *freq, u8 *post_div, u8 *div);
+extern int tda18271_lookup_map(struct dvb_frontend *fe,
+ enum tda18271_map_type map_type,
+ u32 *freq, u8 *val);
+
+extern int tda18271_lookup_thermometer(struct dvb_frontend *fe);
+
+extern int tda18271_lookup_rf_band(struct dvb_frontend *fe,
+ u32 *freq, u8 *rf_band);
+
+extern int tda18271_lookup_cid_target(struct dvb_frontend *fe,
+ u32 *freq, u8 *cid_target,
+ u16 *count_limit);
+
+extern int tda18271_assign_map_layout(struct dvb_frontend *fe);
+
+/*---------------------------------------------------------------------*/
+
+extern int tda18271_read_regs(struct dvb_frontend *fe);
+extern int tda18271_read_extended(struct dvb_frontend *fe);
+extern int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len);
+extern int tda18271_init_regs(struct dvb_frontend *fe);
+
+extern int tda18271_set_standby_mode(struct dvb_frontend *fe,
+ int sm, int sm_lt, int sm_xt);
+
+extern int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq);
+extern int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq);
+
+extern int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq);
+extern int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq);
+extern int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq);
+extern int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq);
+extern int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq);
+extern int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq);
+
+#endif /* __TDA18271_PRIV_H__ */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/tda18271-tables.c b/drivers/media/dvb/frontends/tda18271-tables.c
new file mode 100644
index 000000000000..e94afcfdc5bc
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda18271-tables.c
@@ -0,0 +1,1285 @@
+/*
+ tda18271-tables.c - driver for the Philips / NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "tda18271-priv.h"
+
+struct tda18271_pll_map {
+ u32 lomax;
+ u8 pd; /* post div */
+ u8 d; /* div */
+};
+
+struct tda18271_map {
+ u32 rfmax;
+ u8 val;
+};
+
+/*---------------------------------------------------------------------*/
+
+static struct tda18271_pll_map tda18271c1_main_pll[] = {
+ { .lomax = 32000, .pd = 0x5f, .d = 0xf0 },
+ { .lomax = 35000, .pd = 0x5e, .d = 0xe0 },
+ { .lomax = 37000, .pd = 0x5d, .d = 0xd0 },
+ { .lomax = 41000, .pd = 0x5c, .d = 0xc0 },
+ { .lomax = 44000, .pd = 0x5b, .d = 0xb0 },
+ { .lomax = 49000, .pd = 0x5a, .d = 0xa0 },
+ { .lomax = 54000, .pd = 0x59, .d = 0x90 },
+ { .lomax = 61000, .pd = 0x58, .d = 0x80 },
+ { .lomax = 65000, .pd = 0x4f, .d = 0x78 },
+ { .lomax = 70000, .pd = 0x4e, .d = 0x70 },
+ { .lomax = 75000, .pd = 0x4d, .d = 0x68 },
+ { .lomax = 82000, .pd = 0x4c, .d = 0x60 },
+ { .lomax = 89000, .pd = 0x4b, .d = 0x58 },
+ { .lomax = 98000, .pd = 0x4a, .d = 0x50 },
+ { .lomax = 109000, .pd = 0x49, .d = 0x48 },
+ { .lomax = 123000, .pd = 0x48, .d = 0x40 },
+ { .lomax = 131000, .pd = 0x3f, .d = 0x3c },
+ { .lomax = 141000, .pd = 0x3e, .d = 0x38 },
+ { .lomax = 151000, .pd = 0x3d, .d = 0x34 },
+ { .lomax = 164000, .pd = 0x3c, .d = 0x30 },
+ { .lomax = 179000, .pd = 0x3b, .d = 0x2c },
+ { .lomax = 197000, .pd = 0x3a, .d = 0x28 },
+ { .lomax = 219000, .pd = 0x39, .d = 0x24 },
+ { .lomax = 246000, .pd = 0x38, .d = 0x20 },
+ { .lomax = 263000, .pd = 0x2f, .d = 0x1e },
+ { .lomax = 282000, .pd = 0x2e, .d = 0x1c },
+ { .lomax = 303000, .pd = 0x2d, .d = 0x1a },
+ { .lomax = 329000, .pd = 0x2c, .d = 0x18 },
+ { .lomax = 359000, .pd = 0x2b, .d = 0x16 },
+ { .lomax = 395000, .pd = 0x2a, .d = 0x14 },
+ { .lomax = 438000, .pd = 0x29, .d = 0x12 },
+ { .lomax = 493000, .pd = 0x28, .d = 0x10 },
+ { .lomax = 526000, .pd = 0x1f, .d = 0x0f },
+ { .lomax = 564000, .pd = 0x1e, .d = 0x0e },
+ { .lomax = 607000, .pd = 0x1d, .d = 0x0d },
+ { .lomax = 658000, .pd = 0x1c, .d = 0x0c },
+ { .lomax = 718000, .pd = 0x1b, .d = 0x0b },
+ { .lomax = 790000, .pd = 0x1a, .d = 0x0a },
+ { .lomax = 877000, .pd = 0x19, .d = 0x09 },
+ { .lomax = 987000, .pd = 0x18, .d = 0x08 },
+ { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */
+};
+
+static struct tda18271_pll_map tda18271c2_main_pll[] = {
+ { .lomax = 33125, .pd = 0x57, .d = 0xf0 },
+ { .lomax = 35500, .pd = 0x56, .d = 0xe0 },
+ { .lomax = 38188, .pd = 0x55, .d = 0xd0 },
+ { .lomax = 41375, .pd = 0x54, .d = 0xc0 },
+ { .lomax = 45125, .pd = 0x53, .d = 0xb0 },
+ { .lomax = 49688, .pd = 0x52, .d = 0xa0 },
+ { .lomax = 55188, .pd = 0x51, .d = 0x90 },
+ { .lomax = 62125, .pd = 0x50, .d = 0x80 },
+ { .lomax = 66250, .pd = 0x47, .d = 0x78 },
+ { .lomax = 71000, .pd = 0x46, .d = 0x70 },
+ { .lomax = 76375, .pd = 0x45, .d = 0x68 },
+ { .lomax = 82750, .pd = 0x44, .d = 0x60 },
+ { .lomax = 90250, .pd = 0x43, .d = 0x58 },
+ { .lomax = 99375, .pd = 0x42, .d = 0x50 },
+ { .lomax = 110375, .pd = 0x41, .d = 0x48 },
+ { .lomax = 124250, .pd = 0x40, .d = 0x40 },
+ { .lomax = 132500, .pd = 0x37, .d = 0x3c },
+ { .lomax = 142000, .pd = 0x36, .d = 0x38 },
+ { .lomax = 152750, .pd = 0x35, .d = 0x34 },
+ { .lomax = 165500, .pd = 0x34, .d = 0x30 },
+ { .lomax = 180500, .pd = 0x33, .d = 0x2c },
+ { .lomax = 198750, .pd = 0x32, .d = 0x28 },
+ { .lomax = 220750, .pd = 0x31, .d = 0x24 },
+ { .lomax = 248500, .pd = 0x30, .d = 0x20 },
+ { .lomax = 265000, .pd = 0x27, .d = 0x1e },
+ { .lomax = 284000, .pd = 0x26, .d = 0x1c },
+ { .lomax = 305500, .pd = 0x25, .d = 0x1a },
+ { .lomax = 331000, .pd = 0x24, .d = 0x18 },
+ { .lomax = 361000, .pd = 0x23, .d = 0x16 },
+ { .lomax = 397500, .pd = 0x22, .d = 0x14 },
+ { .lomax = 441500, .pd = 0x21, .d = 0x12 },
+ { .lomax = 497000, .pd = 0x20, .d = 0x10 },
+ { .lomax = 530000, .pd = 0x17, .d = 0x0f },
+ { .lomax = 568000, .pd = 0x16, .d = 0x0e },
+ { .lomax = 611000, .pd = 0x15, .d = 0x0d },
+ { .lomax = 662000, .pd = 0x14, .d = 0x0c },
+ { .lomax = 722000, .pd = 0x13, .d = 0x0b },
+ { .lomax = 795000, .pd = 0x12, .d = 0x0a },
+ { .lomax = 883000, .pd = 0x11, .d = 0x09 },
+ { .lomax = 994000, .pd = 0x10, .d = 0x08 },
+ { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */
+};
+
+static struct tda18271_pll_map tda18271c1_cal_pll[] = {
+ { .lomax = 33000, .pd = 0xdd, .d = 0xd0 },
+ { .lomax = 36000, .pd = 0xdc, .d = 0xc0 },
+ { .lomax = 40000, .pd = 0xdb, .d = 0xb0 },
+ { .lomax = 44000, .pd = 0xda, .d = 0xa0 },
+ { .lomax = 49000, .pd = 0xd9, .d = 0x90 },
+ { .lomax = 55000, .pd = 0xd8, .d = 0x80 },
+ { .lomax = 63000, .pd = 0xd3, .d = 0x70 },
+ { .lomax = 67000, .pd = 0xcd, .d = 0x68 },
+ { .lomax = 73000, .pd = 0xcc, .d = 0x60 },
+ { .lomax = 80000, .pd = 0xcb, .d = 0x58 },
+ { .lomax = 88000, .pd = 0xca, .d = 0x50 },
+ { .lomax = 98000, .pd = 0xc9, .d = 0x48 },
+ { .lomax = 110000, .pd = 0xc8, .d = 0x40 },
+ { .lomax = 126000, .pd = 0xc3, .d = 0x38 },
+ { .lomax = 135000, .pd = 0xbd, .d = 0x34 },
+ { .lomax = 147000, .pd = 0xbc, .d = 0x30 },
+ { .lomax = 160000, .pd = 0xbb, .d = 0x2c },
+ { .lomax = 176000, .pd = 0xba, .d = 0x28 },
+ { .lomax = 196000, .pd = 0xb9, .d = 0x24 },
+ { .lomax = 220000, .pd = 0xb8, .d = 0x20 },
+ { .lomax = 252000, .pd = 0xb3, .d = 0x1c },
+ { .lomax = 271000, .pd = 0xad, .d = 0x1a },
+ { .lomax = 294000, .pd = 0xac, .d = 0x18 },
+ { .lomax = 321000, .pd = 0xab, .d = 0x16 },
+ { .lomax = 353000, .pd = 0xaa, .d = 0x14 },
+ { .lomax = 392000, .pd = 0xa9, .d = 0x12 },
+ { .lomax = 441000, .pd = 0xa8, .d = 0x10 },
+ { .lomax = 505000, .pd = 0xa3, .d = 0x0e },
+ { .lomax = 543000, .pd = 0x9d, .d = 0x0d },
+ { .lomax = 589000, .pd = 0x9c, .d = 0x0c },
+ { .lomax = 642000, .pd = 0x9b, .d = 0x0b },
+ { .lomax = 707000, .pd = 0x9a, .d = 0x0a },
+ { .lomax = 785000, .pd = 0x99, .d = 0x09 },
+ { .lomax = 883000, .pd = 0x98, .d = 0x08 },
+ { .lomax = 1010000, .pd = 0x93, .d = 0x07 },
+ { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */
+};
+
+static struct tda18271_pll_map tda18271c2_cal_pll[] = {
+ { .lomax = 33813, .pd = 0xdd, .d = 0xd0 },
+ { .lomax = 36625, .pd = 0xdc, .d = 0xc0 },
+ { .lomax = 39938, .pd = 0xdb, .d = 0xb0 },
+ { .lomax = 43938, .pd = 0xda, .d = 0xa0 },
+ { .lomax = 48813, .pd = 0xd9, .d = 0x90 },
+ { .lomax = 54938, .pd = 0xd8, .d = 0x80 },
+ { .lomax = 62813, .pd = 0xd3, .d = 0x70 },
+ { .lomax = 67625, .pd = 0xcd, .d = 0x68 },
+ { .lomax = 73250, .pd = 0xcc, .d = 0x60 },
+ { .lomax = 79875, .pd = 0xcb, .d = 0x58 },
+ { .lomax = 87875, .pd = 0xca, .d = 0x50 },
+ { .lomax = 97625, .pd = 0xc9, .d = 0x48 },
+ { .lomax = 109875, .pd = 0xc8, .d = 0x40 },
+ { .lomax = 125625, .pd = 0xc3, .d = 0x38 },
+ { .lomax = 135250, .pd = 0xbd, .d = 0x34 },
+ { .lomax = 146500, .pd = 0xbc, .d = 0x30 },
+ { .lomax = 159750, .pd = 0xbb, .d = 0x2c },
+ { .lomax = 175750, .pd = 0xba, .d = 0x28 },
+ { .lomax = 195250, .pd = 0xb9, .d = 0x24 },
+ { .lomax = 219750, .pd = 0xb8, .d = 0x20 },
+ { .lomax = 251250, .pd = 0xb3, .d = 0x1c },
+ { .lomax = 270500, .pd = 0xad, .d = 0x1a },
+ { .lomax = 293000, .pd = 0xac, .d = 0x18 },
+ { .lomax = 319500, .pd = 0xab, .d = 0x16 },
+ { .lomax = 351500, .pd = 0xaa, .d = 0x14 },
+ { .lomax = 390500, .pd = 0xa9, .d = 0x12 },
+ { .lomax = 439500, .pd = 0xa8, .d = 0x10 },
+ { .lomax = 502500, .pd = 0xa3, .d = 0x0e },
+ { .lomax = 541000, .pd = 0x9d, .d = 0x0d },
+ { .lomax = 586000, .pd = 0x9c, .d = 0x0c },
+ { .lomax = 639000, .pd = 0x9b, .d = 0x0b },
+ { .lomax = 703000, .pd = 0x9a, .d = 0x0a },
+ { .lomax = 781000, .pd = 0x99, .d = 0x09 },
+ { .lomax = 879000, .pd = 0x98, .d = 0x08 },
+ { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271_bp_filter[] = {
+ { .rfmax = 62000, .val = 0x00 },
+ { .rfmax = 84000, .val = 0x01 },
+ { .rfmax = 100000, .val = 0x02 },
+ { .rfmax = 140000, .val = 0x03 },
+ { .rfmax = 170000, .val = 0x04 },
+ { .rfmax = 180000, .val = 0x05 },
+ { .rfmax = 865000, .val = 0x06 },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271c1_km[] = {
+ { .rfmax = 61100, .val = 0x74 },
+ { .rfmax = 350000, .val = 0x40 },
+ { .rfmax = 720000, .val = 0x30 },
+ { .rfmax = 865000, .val = 0x40 },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271c2_km[] = {
+ { .rfmax = 47900, .val = 0x38 },
+ { .rfmax = 61100, .val = 0x44 },
+ { .rfmax = 350000, .val = 0x30 },
+ { .rfmax = 720000, .val = 0x24 },
+ { .rfmax = 865000, .val = 0x3c },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271_rf_band[] = {
+ { .rfmax = 47900, .val = 0x00 },
+ { .rfmax = 61100, .val = 0x01 },
+/* { .rfmax = 152600, .val = 0x02 }, */
+ { .rfmax = 121200, .val = 0x02 },
+ { .rfmax = 164700, .val = 0x03 },
+ { .rfmax = 203500, .val = 0x04 },
+ { .rfmax = 457800, .val = 0x05 },
+ { .rfmax = 865000, .val = 0x06 },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271_gain_taper[] = {
+ { .rfmax = 45400, .val = 0x1f },
+ { .rfmax = 45800, .val = 0x1e },
+ { .rfmax = 46200, .val = 0x1d },
+ { .rfmax = 46700, .val = 0x1c },
+ { .rfmax = 47100, .val = 0x1b },
+ { .rfmax = 47500, .val = 0x1a },
+ { .rfmax = 47900, .val = 0x19 },
+ { .rfmax = 49600, .val = 0x17 },
+ { .rfmax = 51200, .val = 0x16 },
+ { .rfmax = 52900, .val = 0x15 },
+ { .rfmax = 54500, .val = 0x14 },
+ { .rfmax = 56200, .val = 0x13 },
+ { .rfmax = 57800, .val = 0x12 },
+ { .rfmax = 59500, .val = 0x11 },
+ { .rfmax = 61100, .val = 0x10 },
+ { .rfmax = 67600, .val = 0x0d },
+ { .rfmax = 74200, .val = 0x0c },
+ { .rfmax = 80700, .val = 0x0b },
+ { .rfmax = 87200, .val = 0x0a },
+ { .rfmax = 93800, .val = 0x09 },
+ { .rfmax = 100300, .val = 0x08 },
+ { .rfmax = 106900, .val = 0x07 },
+ { .rfmax = 113400, .val = 0x06 },
+ { .rfmax = 119900, .val = 0x05 },
+ { .rfmax = 126500, .val = 0x04 },
+ { .rfmax = 133000, .val = 0x03 },
+ { .rfmax = 139500, .val = 0x02 },
+ { .rfmax = 146100, .val = 0x01 },
+ { .rfmax = 152600, .val = 0x00 },
+ { .rfmax = 154300, .val = 0x1f },
+ { .rfmax = 156100, .val = 0x1e },
+ { .rfmax = 157800, .val = 0x1d },
+ { .rfmax = 159500, .val = 0x1c },
+ { .rfmax = 161200, .val = 0x1b },
+ { .rfmax = 163000, .val = 0x1a },
+ { .rfmax = 164700, .val = 0x19 },
+ { .rfmax = 170200, .val = 0x17 },
+ { .rfmax = 175800, .val = 0x16 },
+ { .rfmax = 181300, .val = 0x15 },
+ { .rfmax = 186900, .val = 0x14 },
+ { .rfmax = 192400, .val = 0x13 },
+ { .rfmax = 198000, .val = 0x12 },
+ { .rfmax = 203500, .val = 0x11 },
+ { .rfmax = 216200, .val = 0x14 },
+ { .rfmax = 228900, .val = 0x13 },
+ { .rfmax = 241600, .val = 0x12 },
+ { .rfmax = 254400, .val = 0x11 },
+ { .rfmax = 267100, .val = 0x10 },
+ { .rfmax = 279800, .val = 0x0f },
+ { .rfmax = 292500, .val = 0x0e },
+ { .rfmax = 305200, .val = 0x0d },
+ { .rfmax = 317900, .val = 0x0c },
+ { .rfmax = 330700, .val = 0x0b },
+ { .rfmax = 343400, .val = 0x0a },
+ { .rfmax = 356100, .val = 0x09 },
+ { .rfmax = 368800, .val = 0x08 },
+ { .rfmax = 381500, .val = 0x07 },
+ { .rfmax = 394200, .val = 0x06 },
+ { .rfmax = 406900, .val = 0x05 },
+ { .rfmax = 419700, .val = 0x04 },
+ { .rfmax = 432400, .val = 0x03 },
+ { .rfmax = 445100, .val = 0x02 },
+ { .rfmax = 457800, .val = 0x01 },
+ { .rfmax = 476300, .val = 0x19 },
+ { .rfmax = 494800, .val = 0x18 },
+ { .rfmax = 513300, .val = 0x17 },
+ { .rfmax = 531800, .val = 0x16 },
+ { .rfmax = 550300, .val = 0x15 },
+ { .rfmax = 568900, .val = 0x14 },
+ { .rfmax = 587400, .val = 0x13 },
+ { .rfmax = 605900, .val = 0x12 },
+ { .rfmax = 624400, .val = 0x11 },
+ { .rfmax = 642900, .val = 0x10 },
+ { .rfmax = 661400, .val = 0x0f },
+ { .rfmax = 679900, .val = 0x0e },
+ { .rfmax = 698400, .val = 0x0d },
+ { .rfmax = 716900, .val = 0x0c },
+ { .rfmax = 735400, .val = 0x0b },
+ { .rfmax = 753900, .val = 0x0a },
+ { .rfmax = 772500, .val = 0x09 },
+ { .rfmax = 791000, .val = 0x08 },
+ { .rfmax = 809500, .val = 0x07 },
+ { .rfmax = 828000, .val = 0x06 },
+ { .rfmax = 846500, .val = 0x05 },
+ { .rfmax = 865000, .val = 0x04 },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271c1_rf_cal[] = {
+ { .rfmax = 41000, .val = 0x1e },
+ { .rfmax = 43000, .val = 0x30 },
+ { .rfmax = 45000, .val = 0x43 },
+ { .rfmax = 46000, .val = 0x4d },
+ { .rfmax = 47000, .val = 0x54 },
+ { .rfmax = 47900, .val = 0x64 },
+ { .rfmax = 49100, .val = 0x20 },
+ { .rfmax = 50000, .val = 0x22 },
+ { .rfmax = 51000, .val = 0x2a },
+ { .rfmax = 53000, .val = 0x32 },
+ { .rfmax = 55000, .val = 0x35 },
+ { .rfmax = 56000, .val = 0x3c },
+ { .rfmax = 57000, .val = 0x3f },
+ { .rfmax = 58000, .val = 0x48 },
+ { .rfmax = 59000, .val = 0x4d },
+ { .rfmax = 60000, .val = 0x58 },
+ { .rfmax = 61100, .val = 0x5f },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271c2_rf_cal[] = {
+ { .rfmax = 41000, .val = 0x0f },
+ { .rfmax = 43000, .val = 0x1c },
+ { .rfmax = 45000, .val = 0x2f },
+ { .rfmax = 46000, .val = 0x39 },
+ { .rfmax = 47000, .val = 0x40 },
+ { .rfmax = 47900, .val = 0x50 },
+ { .rfmax = 49100, .val = 0x16 },
+ { .rfmax = 50000, .val = 0x18 },
+ { .rfmax = 51000, .val = 0x20 },
+ { .rfmax = 53000, .val = 0x28 },
+ { .rfmax = 55000, .val = 0x2b },
+ { .rfmax = 56000, .val = 0x32 },
+ { .rfmax = 57000, .val = 0x35 },
+ { .rfmax = 58000, .val = 0x3e },
+ { .rfmax = 59000, .val = 0x43 },
+ { .rfmax = 60000, .val = 0x4e },
+ { .rfmax = 61100, .val = 0x55 },
+ { .rfmax = 63000, .val = 0x0f },
+ { .rfmax = 64000, .val = 0x11 },
+ { .rfmax = 65000, .val = 0x12 },
+ { .rfmax = 66000, .val = 0x15 },
+ { .rfmax = 67000, .val = 0x16 },
+ { .rfmax = 68000, .val = 0x17 },
+ { .rfmax = 70000, .val = 0x19 },
+ { .rfmax = 71000, .val = 0x1c },
+ { .rfmax = 72000, .val = 0x1d },
+ { .rfmax = 73000, .val = 0x1f },
+ { .rfmax = 74000, .val = 0x20 },
+ { .rfmax = 75000, .val = 0x21 },
+ { .rfmax = 76000, .val = 0x24 },
+ { .rfmax = 77000, .val = 0x25 },
+ { .rfmax = 78000, .val = 0x27 },
+ { .rfmax = 80000, .val = 0x28 },
+ { .rfmax = 81000, .val = 0x29 },
+ { .rfmax = 82000, .val = 0x2d },
+ { .rfmax = 83000, .val = 0x2e },
+ { .rfmax = 84000, .val = 0x2f },
+ { .rfmax = 85000, .val = 0x31 },
+ { .rfmax = 86000, .val = 0x33 },
+ { .rfmax = 87000, .val = 0x34 },
+ { .rfmax = 88000, .val = 0x35 },
+ { .rfmax = 89000, .val = 0x37 },
+ { .rfmax = 90000, .val = 0x38 },
+ { .rfmax = 91000, .val = 0x39 },
+ { .rfmax = 93000, .val = 0x3c },
+ { .rfmax = 94000, .val = 0x3e },
+ { .rfmax = 95000, .val = 0x3f },
+ { .rfmax = 96000, .val = 0x40 },
+ { .rfmax = 97000, .val = 0x42 },
+ { .rfmax = 99000, .val = 0x45 },
+ { .rfmax = 100000, .val = 0x46 },
+ { .rfmax = 102000, .val = 0x48 },
+ { .rfmax = 103000, .val = 0x4a },
+ { .rfmax = 105000, .val = 0x4d },
+ { .rfmax = 106000, .val = 0x4e },
+ { .rfmax = 107000, .val = 0x50 },
+ { .rfmax = 108000, .val = 0x51 },
+ { .rfmax = 110000, .val = 0x54 },
+ { .rfmax = 111000, .val = 0x56 },
+ { .rfmax = 112000, .val = 0x57 },
+ { .rfmax = 113000, .val = 0x58 },
+ { .rfmax = 114000, .val = 0x59 },
+ { .rfmax = 115000, .val = 0x5c },
+ { .rfmax = 116000, .val = 0x5d },
+ { .rfmax = 117000, .val = 0x5f },
+ { .rfmax = 119000, .val = 0x60 },
+ { .rfmax = 120000, .val = 0x64 },
+ { .rfmax = 121000, .val = 0x65 },
+ { .rfmax = 122000, .val = 0x66 },
+ { .rfmax = 123000, .val = 0x68 },
+ { .rfmax = 124000, .val = 0x69 },
+ { .rfmax = 125000, .val = 0x6c },
+ { .rfmax = 126000, .val = 0x6d },
+ { .rfmax = 127000, .val = 0x6e },
+ { .rfmax = 128000, .val = 0x70 },
+ { .rfmax = 129000, .val = 0x71 },
+ { .rfmax = 130000, .val = 0x75 },
+ { .rfmax = 131000, .val = 0x77 },
+ { .rfmax = 132000, .val = 0x78 },
+ { .rfmax = 133000, .val = 0x7b },
+ { .rfmax = 134000, .val = 0x7e },
+ { .rfmax = 135000, .val = 0x81 },
+ { .rfmax = 136000, .val = 0x82 },
+ { .rfmax = 137000, .val = 0x87 },
+ { .rfmax = 138000, .val = 0x88 },
+ { .rfmax = 139000, .val = 0x8d },
+ { .rfmax = 140000, .val = 0x8e },
+ { .rfmax = 141000, .val = 0x91 },
+ { .rfmax = 142000, .val = 0x95 },
+ { .rfmax = 143000, .val = 0x9a },
+ { .rfmax = 144000, .val = 0x9d },
+ { .rfmax = 145000, .val = 0xa1 },
+ { .rfmax = 146000, .val = 0xa2 },
+ { .rfmax = 147000, .val = 0xa4 },
+ { .rfmax = 148000, .val = 0xa9 },
+ { .rfmax = 149000, .val = 0xae },
+ { .rfmax = 150000, .val = 0xb0 },
+ { .rfmax = 151000, .val = 0xb1 },
+ { .rfmax = 152000, .val = 0xb7 },
+ { .rfmax = 153000, .val = 0xbd },
+ { .rfmax = 154000, .val = 0x20 },
+ { .rfmax = 155000, .val = 0x22 },
+ { .rfmax = 156000, .val = 0x24 },
+ { .rfmax = 157000, .val = 0x25 },
+ { .rfmax = 158000, .val = 0x27 },
+ { .rfmax = 159000, .val = 0x29 },
+ { .rfmax = 160000, .val = 0x2c },
+ { .rfmax = 161000, .val = 0x2d },
+ { .rfmax = 163000, .val = 0x2e },
+ { .rfmax = 164000, .val = 0x2f },
+ { .rfmax = 165000, .val = 0x30 },
+ { .rfmax = 166000, .val = 0x11 },
+ { .rfmax = 167000, .val = 0x12 },
+ { .rfmax = 168000, .val = 0x13 },
+ { .rfmax = 169000, .val = 0x14 },
+ { .rfmax = 170000, .val = 0x15 },
+ { .rfmax = 172000, .val = 0x16 },
+ { .rfmax = 173000, .val = 0x17 },
+ { .rfmax = 174000, .val = 0x18 },
+ { .rfmax = 175000, .val = 0x1a },
+ { .rfmax = 176000, .val = 0x1b },
+ { .rfmax = 178000, .val = 0x1d },
+ { .rfmax = 179000, .val = 0x1e },
+ { .rfmax = 180000, .val = 0x1f },
+ { .rfmax = 181000, .val = 0x20 },
+ { .rfmax = 182000, .val = 0x21 },
+ { .rfmax = 183000, .val = 0x22 },
+ { .rfmax = 184000, .val = 0x24 },
+ { .rfmax = 185000, .val = 0x25 },
+ { .rfmax = 186000, .val = 0x26 },
+ { .rfmax = 187000, .val = 0x27 },
+ { .rfmax = 188000, .val = 0x29 },
+ { .rfmax = 189000, .val = 0x2a },
+ { .rfmax = 190000, .val = 0x2c },
+ { .rfmax = 191000, .val = 0x2d },
+ { .rfmax = 192000, .val = 0x2e },
+ { .rfmax = 193000, .val = 0x2f },
+ { .rfmax = 194000, .val = 0x30 },
+ { .rfmax = 195000, .val = 0x33 },
+ { .rfmax = 196000, .val = 0x35 },
+ { .rfmax = 198000, .val = 0x36 },
+ { .rfmax = 200000, .val = 0x38 },
+ { .rfmax = 201000, .val = 0x3c },
+ { .rfmax = 202000, .val = 0x3d },
+ { .rfmax = 203500, .val = 0x3e },
+ { .rfmax = 206000, .val = 0x0e },
+ { .rfmax = 208000, .val = 0x0f },
+ { .rfmax = 212000, .val = 0x10 },
+ { .rfmax = 216000, .val = 0x11 },
+ { .rfmax = 217000, .val = 0x12 },
+ { .rfmax = 218000, .val = 0x13 },
+ { .rfmax = 220000, .val = 0x14 },
+ { .rfmax = 222000, .val = 0x15 },
+ { .rfmax = 225000, .val = 0x16 },
+ { .rfmax = 228000, .val = 0x17 },
+ { .rfmax = 231000, .val = 0x18 },
+ { .rfmax = 234000, .val = 0x19 },
+ { .rfmax = 235000, .val = 0x1a },
+ { .rfmax = 236000, .val = 0x1b },
+ { .rfmax = 237000, .val = 0x1c },
+ { .rfmax = 240000, .val = 0x1d },
+ { .rfmax = 242000, .val = 0x1f },
+ { .rfmax = 247000, .val = 0x20 },
+ { .rfmax = 249000, .val = 0x21 },
+ { .rfmax = 252000, .val = 0x22 },
+ { .rfmax = 253000, .val = 0x23 },
+ { .rfmax = 254000, .val = 0x24 },
+ { .rfmax = 256000, .val = 0x25 },
+ { .rfmax = 259000, .val = 0x26 },
+ { .rfmax = 262000, .val = 0x27 },
+ { .rfmax = 264000, .val = 0x28 },
+ { .rfmax = 267000, .val = 0x29 },
+ { .rfmax = 269000, .val = 0x2a },
+ { .rfmax = 271000, .val = 0x2b },
+ { .rfmax = 273000, .val = 0x2c },
+ { .rfmax = 275000, .val = 0x2d },
+ { .rfmax = 277000, .val = 0x2e },
+ { .rfmax = 279000, .val = 0x2f },
+ { .rfmax = 282000, .val = 0x30 },
+ { .rfmax = 284000, .val = 0x31 },
+ { .rfmax = 286000, .val = 0x32 },
+ { .rfmax = 287000, .val = 0x33 },
+ { .rfmax = 290000, .val = 0x34 },
+ { .rfmax = 293000, .val = 0x35 },
+ { .rfmax = 295000, .val = 0x36 },
+ { .rfmax = 297000, .val = 0x37 },
+ { .rfmax = 300000, .val = 0x38 },
+ { .rfmax = 303000, .val = 0x39 },
+ { .rfmax = 305000, .val = 0x3a },
+ { .rfmax = 306000, .val = 0x3b },
+ { .rfmax = 307000, .val = 0x3c },
+ { .rfmax = 310000, .val = 0x3d },
+ { .rfmax = 312000, .val = 0x3e },
+ { .rfmax = 315000, .val = 0x3f },
+ { .rfmax = 318000, .val = 0x40 },
+ { .rfmax = 320000, .val = 0x41 },
+ { .rfmax = 323000, .val = 0x42 },
+ { .rfmax = 324000, .val = 0x43 },
+ { .rfmax = 325000, .val = 0x44 },
+ { .rfmax = 327000, .val = 0x45 },
+ { .rfmax = 331000, .val = 0x46 },
+ { .rfmax = 334000, .val = 0x47 },
+ { .rfmax = 337000, .val = 0x48 },
+ { .rfmax = 339000, .val = 0x49 },
+ { .rfmax = 340000, .val = 0x4a },
+ { .rfmax = 341000, .val = 0x4b },
+ { .rfmax = 343000, .val = 0x4c },
+ { .rfmax = 345000, .val = 0x4d },
+ { .rfmax = 349000, .val = 0x4e },
+ { .rfmax = 352000, .val = 0x4f },
+ { .rfmax = 353000, .val = 0x50 },
+ { .rfmax = 355000, .val = 0x51 },
+ { .rfmax = 357000, .val = 0x52 },
+ { .rfmax = 359000, .val = 0x53 },
+ { .rfmax = 361000, .val = 0x54 },
+ { .rfmax = 362000, .val = 0x55 },
+ { .rfmax = 364000, .val = 0x56 },
+ { .rfmax = 368000, .val = 0x57 },
+ { .rfmax = 370000, .val = 0x58 },
+ { .rfmax = 372000, .val = 0x59 },
+ { .rfmax = 375000, .val = 0x5a },
+ { .rfmax = 376000, .val = 0x5b },
+ { .rfmax = 377000, .val = 0x5c },
+ { .rfmax = 379000, .val = 0x5d },
+ { .rfmax = 382000, .val = 0x5e },
+ { .rfmax = 384000, .val = 0x5f },
+ { .rfmax = 385000, .val = 0x60 },
+ { .rfmax = 386000, .val = 0x61 },
+ { .rfmax = 388000, .val = 0x62 },
+ { .rfmax = 390000, .val = 0x63 },
+ { .rfmax = 393000, .val = 0x64 },
+ { .rfmax = 394000, .val = 0x65 },
+ { .rfmax = 396000, .val = 0x66 },
+ { .rfmax = 397000, .val = 0x67 },
+ { .rfmax = 398000, .val = 0x68 },
+ { .rfmax = 400000, .val = 0x69 },
+ { .rfmax = 402000, .val = 0x6a },
+ { .rfmax = 403000, .val = 0x6b },
+ { .rfmax = 407000, .val = 0x6c },
+ { .rfmax = 408000, .val = 0x6d },
+ { .rfmax = 409000, .val = 0x6e },
+ { .rfmax = 410000, .val = 0x6f },
+ { .rfmax = 411000, .val = 0x70 },
+ { .rfmax = 412000, .val = 0x71 },
+ { .rfmax = 413000, .val = 0x72 },
+ { .rfmax = 414000, .val = 0x73 },
+ { .rfmax = 417000, .val = 0x74 },
+ { .rfmax = 418000, .val = 0x75 },
+ { .rfmax = 420000, .val = 0x76 },
+ { .rfmax = 422000, .val = 0x77 },
+ { .rfmax = 423000, .val = 0x78 },
+ { .rfmax = 424000, .val = 0x79 },
+ { .rfmax = 427000, .val = 0x7a },
+ { .rfmax = 428000, .val = 0x7b },
+ { .rfmax = 429000, .val = 0x7d },
+ { .rfmax = 432000, .val = 0x7f },
+ { .rfmax = 434000, .val = 0x80 },
+ { .rfmax = 435000, .val = 0x81 },
+ { .rfmax = 436000, .val = 0x83 },
+ { .rfmax = 437000, .val = 0x84 },
+ { .rfmax = 438000, .val = 0x85 },
+ { .rfmax = 439000, .val = 0x86 },
+ { .rfmax = 440000, .val = 0x87 },
+ { .rfmax = 441000, .val = 0x88 },
+ { .rfmax = 442000, .val = 0x89 },
+ { .rfmax = 445000, .val = 0x8a },
+ { .rfmax = 446000, .val = 0x8b },
+ { .rfmax = 447000, .val = 0x8c },
+ { .rfmax = 448000, .val = 0x8e },
+ { .rfmax = 449000, .val = 0x8f },
+ { .rfmax = 450000, .val = 0x90 },
+ { .rfmax = 452000, .val = 0x91 },
+ { .rfmax = 453000, .val = 0x93 },
+ { .rfmax = 454000, .val = 0x94 },
+ { .rfmax = 456000, .val = 0x96 },
+ { .rfmax = 457000, .val = 0x98 },
+ { .rfmax = 461000, .val = 0x11 },
+ { .rfmax = 468000, .val = 0x12 },
+ { .rfmax = 472000, .val = 0x13 },
+ { .rfmax = 473000, .val = 0x14 },
+ { .rfmax = 474000, .val = 0x15 },
+ { .rfmax = 481000, .val = 0x16 },
+ { .rfmax = 486000, .val = 0x17 },
+ { .rfmax = 491000, .val = 0x18 },
+ { .rfmax = 498000, .val = 0x19 },
+ { .rfmax = 499000, .val = 0x1a },
+ { .rfmax = 501000, .val = 0x1b },
+ { .rfmax = 506000, .val = 0x1c },
+ { .rfmax = 511000, .val = 0x1d },
+ { .rfmax = 516000, .val = 0x1e },
+ { .rfmax = 520000, .val = 0x1f },
+ { .rfmax = 521000, .val = 0x20 },
+ { .rfmax = 525000, .val = 0x21 },
+ { .rfmax = 529000, .val = 0x22 },
+ { .rfmax = 533000, .val = 0x23 },
+ { .rfmax = 539000, .val = 0x24 },
+ { .rfmax = 541000, .val = 0x25 },
+ { .rfmax = 547000, .val = 0x26 },
+ { .rfmax = 549000, .val = 0x27 },
+ { .rfmax = 551000, .val = 0x28 },
+ { .rfmax = 556000, .val = 0x29 },
+ { .rfmax = 561000, .val = 0x2a },
+ { .rfmax = 563000, .val = 0x2b },
+ { .rfmax = 565000, .val = 0x2c },
+ { .rfmax = 569000, .val = 0x2d },
+ { .rfmax = 571000, .val = 0x2e },
+ { .rfmax = 577000, .val = 0x2f },
+ { .rfmax = 580000, .val = 0x30 },
+ { .rfmax = 582000, .val = 0x31 },
+ { .rfmax = 584000, .val = 0x32 },
+ { .rfmax = 588000, .val = 0x33 },
+ { .rfmax = 591000, .val = 0x34 },
+ { .rfmax = 596000, .val = 0x35 },
+ { .rfmax = 598000, .val = 0x36 },
+ { .rfmax = 603000, .val = 0x37 },
+ { .rfmax = 604000, .val = 0x38 },
+ { .rfmax = 606000, .val = 0x39 },
+ { .rfmax = 612000, .val = 0x3a },
+ { .rfmax = 615000, .val = 0x3b },
+ { .rfmax = 617000, .val = 0x3c },
+ { .rfmax = 621000, .val = 0x3d },
+ { .rfmax = 622000, .val = 0x3e },
+ { .rfmax = 625000, .val = 0x3f },
+ { .rfmax = 632000, .val = 0x40 },
+ { .rfmax = 633000, .val = 0x41 },
+ { .rfmax = 634000, .val = 0x42 },
+ { .rfmax = 642000, .val = 0x43 },
+ { .rfmax = 643000, .val = 0x44 },
+ { .rfmax = 647000, .val = 0x45 },
+ { .rfmax = 650000, .val = 0x46 },
+ { .rfmax = 652000, .val = 0x47 },
+ { .rfmax = 657000, .val = 0x48 },
+ { .rfmax = 661000, .val = 0x49 },
+ { .rfmax = 662000, .val = 0x4a },
+ { .rfmax = 665000, .val = 0x4b },
+ { .rfmax = 667000, .val = 0x4c },
+ { .rfmax = 670000, .val = 0x4d },
+ { .rfmax = 673000, .val = 0x4e },
+ { .rfmax = 676000, .val = 0x4f },
+ { .rfmax = 677000, .val = 0x50 },
+ { .rfmax = 681000, .val = 0x51 },
+ { .rfmax = 683000, .val = 0x52 },
+ { .rfmax = 686000, .val = 0x53 },
+ { .rfmax = 688000, .val = 0x54 },
+ { .rfmax = 689000, .val = 0x55 },
+ { .rfmax = 691000, .val = 0x56 },
+ { .rfmax = 695000, .val = 0x57 },
+ { .rfmax = 698000, .val = 0x58 },
+ { .rfmax = 703000, .val = 0x59 },
+ { .rfmax = 704000, .val = 0x5a },
+ { .rfmax = 705000, .val = 0x5b },
+ { .rfmax = 707000, .val = 0x5c },
+ { .rfmax = 710000, .val = 0x5d },
+ { .rfmax = 712000, .val = 0x5e },
+ { .rfmax = 717000, .val = 0x5f },
+ { .rfmax = 718000, .val = 0x60 },
+ { .rfmax = 721000, .val = 0x61 },
+ { .rfmax = 722000, .val = 0x62 },
+ { .rfmax = 723000, .val = 0x63 },
+ { .rfmax = 725000, .val = 0x64 },
+ { .rfmax = 727000, .val = 0x65 },
+ { .rfmax = 730000, .val = 0x66 },
+ { .rfmax = 732000, .val = 0x67 },
+ { .rfmax = 735000, .val = 0x68 },
+ { .rfmax = 740000, .val = 0x69 },
+ { .rfmax = 741000, .val = 0x6a },
+ { .rfmax = 742000, .val = 0x6b },
+ { .rfmax = 743000, .val = 0x6c },
+ { .rfmax = 745000, .val = 0x6d },
+ { .rfmax = 747000, .val = 0x6e },
+ { .rfmax = 748000, .val = 0x6f },
+ { .rfmax = 750000, .val = 0x70 },
+ { .rfmax = 752000, .val = 0x71 },
+ { .rfmax = 754000, .val = 0x72 },
+ { .rfmax = 757000, .val = 0x73 },
+ { .rfmax = 758000, .val = 0x74 },
+ { .rfmax = 760000, .val = 0x75 },
+ { .rfmax = 763000, .val = 0x76 },
+ { .rfmax = 764000, .val = 0x77 },
+ { .rfmax = 766000, .val = 0x78 },
+ { .rfmax = 767000, .val = 0x79 },
+ { .rfmax = 768000, .val = 0x7a },
+ { .rfmax = 773000, .val = 0x7b },
+ { .rfmax = 774000, .val = 0x7c },
+ { .rfmax = 776000, .val = 0x7d },
+ { .rfmax = 777000, .val = 0x7e },
+ { .rfmax = 778000, .val = 0x7f },
+ { .rfmax = 779000, .val = 0x80 },
+ { .rfmax = 781000, .val = 0x81 },
+ { .rfmax = 783000, .val = 0x82 },
+ { .rfmax = 784000, .val = 0x83 },
+ { .rfmax = 785000, .val = 0x84 },
+ { .rfmax = 786000, .val = 0x85 },
+ { .rfmax = 793000, .val = 0x86 },
+ { .rfmax = 794000, .val = 0x87 },
+ { .rfmax = 795000, .val = 0x88 },
+ { .rfmax = 797000, .val = 0x89 },
+ { .rfmax = 799000, .val = 0x8a },
+ { .rfmax = 801000, .val = 0x8b },
+ { .rfmax = 802000, .val = 0x8c },
+ { .rfmax = 803000, .val = 0x8d },
+ { .rfmax = 804000, .val = 0x8e },
+ { .rfmax = 810000, .val = 0x90 },
+ { .rfmax = 811000, .val = 0x91 },
+ { .rfmax = 812000, .val = 0x92 },
+ { .rfmax = 814000, .val = 0x93 },
+ { .rfmax = 816000, .val = 0x94 },
+ { .rfmax = 817000, .val = 0x96 },
+ { .rfmax = 818000, .val = 0x97 },
+ { .rfmax = 820000, .val = 0x98 },
+ { .rfmax = 821000, .val = 0x99 },
+ { .rfmax = 822000, .val = 0x9a },
+ { .rfmax = 828000, .val = 0x9b },
+ { .rfmax = 829000, .val = 0x9d },
+ { .rfmax = 830000, .val = 0x9f },
+ { .rfmax = 831000, .val = 0xa0 },
+ { .rfmax = 833000, .val = 0xa1 },
+ { .rfmax = 835000, .val = 0xa2 },
+ { .rfmax = 836000, .val = 0xa3 },
+ { .rfmax = 837000, .val = 0xa4 },
+ { .rfmax = 838000, .val = 0xa6 },
+ { .rfmax = 840000, .val = 0xa8 },
+ { .rfmax = 842000, .val = 0xa9 },
+ { .rfmax = 845000, .val = 0xaa },
+ { .rfmax = 846000, .val = 0xab },
+ { .rfmax = 847000, .val = 0xad },
+ { .rfmax = 848000, .val = 0xae },
+ { .rfmax = 852000, .val = 0xaf },
+ { .rfmax = 853000, .val = 0xb0 },
+ { .rfmax = 858000, .val = 0xb1 },
+ { .rfmax = 860000, .val = 0xb2 },
+ { .rfmax = 861000, .val = 0xb3 },
+ { .rfmax = 862000, .val = 0xb4 },
+ { .rfmax = 863000, .val = 0xb6 },
+ { .rfmax = 864000, .val = 0xb8 },
+ { .rfmax = 865000, .val = 0xb9 },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+static struct tda18271_map tda18271_ir_measure[] = {
+ { .rfmax = 30000, .val = 4 },
+ { .rfmax = 200000, .val = 5 },
+ { .rfmax = 600000, .val = 6 },
+ { .rfmax = 865000, .val = 7 },
+ { .rfmax = 0, .val = 0 }, /* end */
+};
+
+static struct tda18271_map tda18271_rf_cal_dc_over_dt[] = {
+ { .rfmax = 47900, .val = 0x00 },
+ { .rfmax = 55000, .val = 0x00 },
+ { .rfmax = 61100, .val = 0x0a },
+ { .rfmax = 64000, .val = 0x0a },
+ { .rfmax = 82000, .val = 0x14 },
+ { .rfmax = 84000, .val = 0x19 },
+ { .rfmax = 119000, .val = 0x1c },
+ { .rfmax = 124000, .val = 0x20 },
+ { .rfmax = 129000, .val = 0x2a },
+ { .rfmax = 134000, .val = 0x32 },
+ { .rfmax = 139000, .val = 0x39 },
+ { .rfmax = 144000, .val = 0x3e },
+ { .rfmax = 149000, .val = 0x3f },
+ { .rfmax = 152600, .val = 0x40 },
+ { .rfmax = 154000, .val = 0x40 },
+ { .rfmax = 164700, .val = 0x41 },
+ { .rfmax = 203500, .val = 0x32 },
+ { .rfmax = 353000, .val = 0x19 },
+ { .rfmax = 356000, .val = 0x1a },
+ { .rfmax = 359000, .val = 0x1b },
+ { .rfmax = 363000, .val = 0x1c },
+ { .rfmax = 366000, .val = 0x1d },
+ { .rfmax = 369000, .val = 0x1e },
+ { .rfmax = 373000, .val = 0x1f },
+ { .rfmax = 376000, .val = 0x20 },
+ { .rfmax = 379000, .val = 0x21 },
+ { .rfmax = 383000, .val = 0x22 },
+ { .rfmax = 386000, .val = 0x23 },
+ { .rfmax = 389000, .val = 0x24 },
+ { .rfmax = 393000, .val = 0x25 },
+ { .rfmax = 396000, .val = 0x26 },
+ { .rfmax = 399000, .val = 0x27 },
+ { .rfmax = 402000, .val = 0x28 },
+ { .rfmax = 404000, .val = 0x29 },
+ { .rfmax = 407000, .val = 0x2a },
+ { .rfmax = 409000, .val = 0x2b },
+ { .rfmax = 412000, .val = 0x2c },
+ { .rfmax = 414000, .val = 0x2d },
+ { .rfmax = 417000, .val = 0x2e },
+ { .rfmax = 419000, .val = 0x2f },
+ { .rfmax = 422000, .val = 0x30 },
+ { .rfmax = 424000, .val = 0x31 },
+ { .rfmax = 427000, .val = 0x32 },
+ { .rfmax = 429000, .val = 0x33 },
+ { .rfmax = 432000, .val = 0x34 },
+ { .rfmax = 434000, .val = 0x35 },
+ { .rfmax = 437000, .val = 0x36 },
+ { .rfmax = 439000, .val = 0x37 },
+ { .rfmax = 442000, .val = 0x38 },
+ { .rfmax = 444000, .val = 0x39 },
+ { .rfmax = 447000, .val = 0x3a },
+ { .rfmax = 449000, .val = 0x3b },
+ { .rfmax = 457800, .val = 0x3c },
+ { .rfmax = 465000, .val = 0x0f },
+ { .rfmax = 477000, .val = 0x12 },
+ { .rfmax = 483000, .val = 0x14 },
+ { .rfmax = 502000, .val = 0x19 },
+ { .rfmax = 508000, .val = 0x1b },
+ { .rfmax = 519000, .val = 0x1c },
+ { .rfmax = 522000, .val = 0x1d },
+ { .rfmax = 524000, .val = 0x1e },
+ { .rfmax = 534000, .val = 0x1f },
+ { .rfmax = 549000, .val = 0x20 },
+ { .rfmax = 554000, .val = 0x22 },
+ { .rfmax = 584000, .val = 0x24 },
+ { .rfmax = 589000, .val = 0x26 },
+ { .rfmax = 658000, .val = 0x27 },
+ { .rfmax = 664000, .val = 0x2c },
+ { .rfmax = 669000, .val = 0x2d },
+ { .rfmax = 699000, .val = 0x2e },
+ { .rfmax = 704000, .val = 0x30 },
+ { .rfmax = 709000, .val = 0x31 },
+ { .rfmax = 714000, .val = 0x32 },
+ { .rfmax = 724000, .val = 0x33 },
+ { .rfmax = 729000, .val = 0x36 },
+ { .rfmax = 739000, .val = 0x38 },
+ { .rfmax = 744000, .val = 0x39 },
+ { .rfmax = 749000, .val = 0x3b },
+ { .rfmax = 754000, .val = 0x3c },
+ { .rfmax = 759000, .val = 0x3d },
+ { .rfmax = 764000, .val = 0x3e },
+ { .rfmax = 769000, .val = 0x3f },
+ { .rfmax = 774000, .val = 0x40 },
+ { .rfmax = 779000, .val = 0x41 },
+ { .rfmax = 784000, .val = 0x43 },
+ { .rfmax = 789000, .val = 0x46 },
+ { .rfmax = 794000, .val = 0x48 },
+ { .rfmax = 799000, .val = 0x4b },
+ { .rfmax = 804000, .val = 0x4f },
+ { .rfmax = 809000, .val = 0x54 },
+ { .rfmax = 814000, .val = 0x59 },
+ { .rfmax = 819000, .val = 0x5d },
+ { .rfmax = 824000, .val = 0x61 },
+ { .rfmax = 829000, .val = 0x68 },
+ { .rfmax = 834000, .val = 0x6e },
+ { .rfmax = 839000, .val = 0x75 },
+ { .rfmax = 844000, .val = 0x7e },
+ { .rfmax = 849000, .val = 0x82 },
+ { .rfmax = 854000, .val = 0x84 },
+ { .rfmax = 859000, .val = 0x8f },
+ { .rfmax = 865000, .val = 0x9a },
+ { .rfmax = 0, .val = 0x00 }, /* end */
+};
+
+/*---------------------------------------------------------------------*/
+
+struct tda18271_thermo_map {
+ u8 d;
+ u8 r0;
+ u8 r1;
+};
+
+static struct tda18271_thermo_map tda18271_thermometer[] = {
+ { .d = 0x00, .r0 = 60, .r1 = 92 },
+ { .d = 0x01, .r0 = 62, .r1 = 94 },
+ { .d = 0x02, .r0 = 66, .r1 = 98 },
+ { .d = 0x03, .r0 = 64, .r1 = 96 },
+ { .d = 0x04, .r0 = 74, .r1 = 106 },
+ { .d = 0x05, .r0 = 72, .r1 = 104 },
+ { .d = 0x06, .r0 = 68, .r1 = 100 },
+ { .d = 0x07, .r0 = 70, .r1 = 102 },
+ { .d = 0x08, .r0 = 90, .r1 = 122 },
+ { .d = 0x09, .r0 = 88, .r1 = 120 },
+ { .d = 0x0a, .r0 = 84, .r1 = 116 },
+ { .d = 0x0b, .r0 = 86, .r1 = 118 },
+ { .d = 0x0c, .r0 = 76, .r1 = 108 },
+ { .d = 0x0d, .r0 = 78, .r1 = 110 },
+ { .d = 0x0e, .r0 = 82, .r1 = 114 },
+ { .d = 0x0f, .r0 = 80, .r1 = 112 },
+ { .d = 0x00, .r0 = 0, .r1 = 0 }, /* end */
+};
+
+int tda18271_lookup_thermometer(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int val, i = 0;
+
+ while (tda18271_thermometer[i].d < (regs[R_TM] & 0x0f)) {
+ if (tda18271_thermometer[i + 1].d == 0)
+ break;
+ i++;
+ }
+
+ if ((regs[R_TM] & 0x20) == 0x20)
+ val = tda18271_thermometer[i].r1;
+ else
+ val = tda18271_thermometer[i].r0;
+
+ tda_map("(%d) tm = %d\n", i, val);
+
+ return val;
+}
+
+/*---------------------------------------------------------------------*/
+
+struct tda18271_cid_target_map {
+ u32 rfmax;
+ u8 target;
+ u16 limit;
+};
+
+static struct tda18271_cid_target_map tda18271_cid_target[] = {
+ { .rfmax = 46000, .target = 0x04, .limit = 1800 },
+ { .rfmax = 52200, .target = 0x0a, .limit = 1500 },
+ { .rfmax = 79100, .target = 0x01, .limit = 4000 },
+ { .rfmax = 136800, .target = 0x18, .limit = 4000 },
+ { .rfmax = 156700, .target = 0x18, .limit = 4000 },
+ { .rfmax = 156700, .target = 0x18, .limit = 4000 },
+ { .rfmax = 186250, .target = 0x0a, .limit = 4000 },
+ { .rfmax = 230000, .target = 0x0a, .limit = 4000 },
+ { .rfmax = 345000, .target = 0x18, .limit = 4000 },
+ { .rfmax = 426000, .target = 0x0e, .limit = 4000 },
+ { .rfmax = 489500, .target = 0x1e, .limit = 4000 },
+ { .rfmax = 697500, .target = 0x32, .limit = 4000 },
+ { .rfmax = 842000, .target = 0x3a, .limit = 4000 },
+ { .rfmax = 0, .target = 0x00, .limit = 0 }, /* end */
+};
+
+int tda18271_lookup_cid_target(struct dvb_frontend *fe,
+ u32 *freq, u8 *cid_target, u16 *count_limit)
+{
+ int i = 0;
+
+ while ((tda18271_cid_target[i].rfmax * 1000) < *freq) {
+ if (tda18271_cid_target[i + 1].rfmax == 0)
+ break;
+ i++;
+ }
+ *cid_target = tda18271_cid_target[i].target;
+ *count_limit = tda18271_cid_target[i].limit;
+
+ tda_map("(%d) cid_target = %02x, count_limit = %d\n", i,
+ tda18271_cid_target[i].target, tda18271_cid_target[i].limit);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+static struct tda18271_rf_tracking_filter_cal tda18271_rf_band_template[] = {
+ { .rfmax = 47900, .rfband = 0x00,
+ .rf1_def = 46000, .rf2_def = 0, .rf3_def = 0 },
+ { .rfmax = 61100, .rfband = 0x01,
+ .rf1_def = 52200, .rf2_def = 0, .rf3_def = 0 },
+ { .rfmax = 152600, .rfband = 0x02,
+ .rf1_def = 70100, .rf2_def = 136800, .rf3_def = 0 },
+ { .rfmax = 164700, .rfband = 0x03,
+ .rf1_def = 156700, .rf2_def = 0, .rf3_def = 0 },
+ { .rfmax = 203500, .rfband = 0x04,
+ .rf1_def = 186250, .rf2_def = 0, .rf3_def = 0 },
+ { .rfmax = 457800, .rfband = 0x05,
+ .rf1_def = 230000, .rf2_def = 345000, .rf3_def = 426000 },
+ { .rfmax = 865000, .rfband = 0x06,
+ .rf1_def = 489500, .rf2_def = 697500, .rf3_def = 842000 },
+ { .rfmax = 0, .rfband = 0x00,
+ .rf1_def = 0, .rf2_def = 0, .rf3_def = 0 }, /* end */
+};
+
+int tda18271_lookup_rf_band(struct dvb_frontend *fe, u32 *freq, u8 *rf_band)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
+ int i = 0;
+
+ while ((map[i].rfmax * 1000) < *freq) {
+ if (tda18271_debug & DBG_ADV)
+ tda_map("(%d) rfmax = %d < freq = %d, "
+ "rf1_def = %d, rf2_def = %d, rf3_def = %d, "
+ "rf1 = %d, rf2 = %d, rf3 = %d, "
+ "rf_a1 = %d, rf_a2 = %d, "
+ "rf_b1 = %d, rf_b2 = %d\n",
+ i, map[i].rfmax * 1000, *freq,
+ map[i].rf1_def, map[i].rf2_def, map[i].rf3_def,
+ map[i].rf1, map[i].rf2, map[i].rf3,
+ map[i].rf_a1, map[i].rf_a2,
+ map[i].rf_b1, map[i].rf_b2);
+ if (map[i].rfmax == 0)
+ return -EINVAL;
+ i++;
+ }
+ if (rf_band)
+ *rf_band = map[i].rfband;
+
+ tda_map("(%d) rf_band = %02x\n", i, map[i].rfband);
+
+ return i;
+}
+
+/*---------------------------------------------------------------------*/
+
+struct tda18271_map_layout {
+ struct tda18271_pll_map *main_pll;
+ struct tda18271_pll_map *cal_pll;
+
+ struct tda18271_map *rf_cal;
+ struct tda18271_map *rf_cal_kmco;
+ struct tda18271_map *rf_cal_dc_over_dt;
+
+ struct tda18271_map *bp_filter;
+ struct tda18271_map *rf_band;
+ struct tda18271_map *gain_taper;
+ struct tda18271_map *ir_measure;
+};
+
+/*---------------------------------------------------------------------*/
+
+int tda18271_lookup_pll_map(struct dvb_frontend *fe,
+ enum tda18271_map_type map_type,
+ u32 *freq, u8 *post_div, u8 *div)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_pll_map *map = NULL;
+ unsigned int i = 0;
+ char *map_name;
+ int ret = 0;
+
+ BUG_ON(!priv->maps);
+
+ switch (map_type) {
+ case MAIN_PLL:
+ map = priv->maps->main_pll;
+ map_name = "main_pll";
+ break;
+ case CAL_PLL:
+ map = priv->maps->cal_pll;
+ map_name = "cal_pll";
+ break;
+ default:
+ /* we should never get here */
+ map_name = "undefined";
+ break;
+ }
+
+ if (!map) {
+ tda_warn("%s map is not set!\n", map_name);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ while ((map[i].lomax * 1000) < *freq) {
+ if (map[i + 1].lomax == 0) {
+ tda_map("%s: frequency (%d) out of range\n",
+ map_name, *freq);
+ ret = -ERANGE;
+ break;
+ }
+ i++;
+ }
+ *post_div = map[i].pd;
+ *div = map[i].d;
+
+ tda_map("(%d) %s: post div = 0x%02x, div = 0x%02x\n",
+ i, map_name, *post_div, *div);
+fail:
+ return ret;
+}
+
+int tda18271_lookup_map(struct dvb_frontend *fe,
+ enum tda18271_map_type map_type,
+ u32 *freq, u8 *val)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_map *map = NULL;
+ unsigned int i = 0;
+ char *map_name;
+ int ret = 0;
+
+ BUG_ON(!priv->maps);
+
+ switch (map_type) {
+ case BP_FILTER:
+ map = priv->maps->bp_filter;
+ map_name = "bp_filter";
+ break;
+ case RF_CAL_KMCO:
+ map = priv->maps->rf_cal_kmco;
+ map_name = "km";
+ break;
+ case RF_BAND:
+ map = priv->maps->rf_band;
+ map_name = "rf_band";
+ break;
+ case GAIN_TAPER:
+ map = priv->maps->gain_taper;
+ map_name = "gain_taper";
+ break;
+ case RF_CAL:
+ map = priv->maps->rf_cal;
+ map_name = "rf_cal";
+ break;
+ case IR_MEASURE:
+ map = priv->maps->ir_measure;
+ map_name = "ir_measure";
+ break;
+ case RF_CAL_DC_OVER_DT:
+ map = priv->maps->rf_cal_dc_over_dt;
+ map_name = "rf_cal_dc_over_dt";
+ break;
+ default:
+ /* we should never get here */
+ map_name = "undefined";
+ break;
+ }
+
+ if (!map) {
+ tda_warn("%s map is not set!\n", map_name);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ while ((map[i].rfmax * 1000) < *freq) {
+ if (map[i + 1].rfmax == 0) {
+ tda_map("%s: frequency (%d) out of range\n",
+ map_name, *freq);
+ ret = -ERANGE;
+ break;
+ }
+ i++;
+ }
+ *val = map[i].val;
+
+ tda_map("(%d) %s: 0x%02x\n", i, map_name, *val);
+fail:
+ return ret;
+}
+
+/*---------------------------------------------------------------------*/
+
+static struct tda18271_std_map tda18271c1_std_map = {
+ .fm_radio = { .if_freq = 1250, .std_bits = 0x18 },
+ .atv_b = { .if_freq = 6750, .std_bits = 0x0e },
+ .atv_dk = { .if_freq = 7750, .std_bits = 0x0f },
+ .atv_gh = { .if_freq = 7750, .std_bits = 0x0f },
+ .atv_i = { .if_freq = 7750, .std_bits = 0x0f },
+ .atv_l = { .if_freq = 7750, .std_bits = 0x0f },
+ .atv_lc = { .if_freq = 1250, .std_bits = 0x0f },
+ .atv_mn = { .if_freq = 5750, .std_bits = 0x0d },
+ .atsc_6 = { .if_freq = 3250, .std_bits = 0x1c },
+ .dvbt_6 = { .if_freq = 3300, .std_bits = 0x1c },
+ .dvbt_7 = { .if_freq = 3800, .std_bits = 0x1d },
+ .dvbt_8 = { .if_freq = 4300, .std_bits = 0x1e },
+ .qam_6 = { .if_freq = 4000, .std_bits = 0x1d },
+ .qam_8 = { .if_freq = 5000, .std_bits = 0x1f },
+};
+
+static struct tda18271_std_map tda18271c2_std_map = {
+ .fm_radio = { .if_freq = 1250, .std_bits = 0x18 },
+ .atv_b = { .if_freq = 6000, .std_bits = 0x0d },
+ .atv_dk = { .if_freq = 6900, .std_bits = 0x0e },
+ .atv_gh = { .if_freq = 7100, .std_bits = 0x0e },
+ .atv_i = { .if_freq = 7250, .std_bits = 0x0e },
+ .atv_l = { .if_freq = 6900, .std_bits = 0x0e },
+ .atv_lc = { .if_freq = 1250, .std_bits = 0x0e },
+ .atv_mn = { .if_freq = 5400, .std_bits = 0x0c },
+ .atsc_6 = { .if_freq = 3250, .std_bits = 0x1c },
+ .dvbt_6 = { .if_freq = 3300, .std_bits = 0x1c },
+ .dvbt_7 = { .if_freq = 3500, .std_bits = 0x1c },
+ .dvbt_8 = { .if_freq = 4000, .std_bits = 0x1d },
+ .qam_6 = { .if_freq = 4000, .std_bits = 0x1d },
+ .qam_8 = { .if_freq = 5000, .std_bits = 0x1f },
+};
+
+/*---------------------------------------------------------------------*/
+
+static struct tda18271_map_layout tda18271c1_map_layout = {
+ .main_pll = tda18271c1_main_pll,
+ .cal_pll = tda18271c1_cal_pll,
+
+ .rf_cal = tda18271c1_rf_cal,
+ .rf_cal_kmco = tda18271c1_km,
+
+ .bp_filter = tda18271_bp_filter,
+ .rf_band = tda18271_rf_band,
+ .gain_taper = tda18271_gain_taper,
+ .ir_measure = tda18271_ir_measure,
+};
+
+static struct tda18271_map_layout tda18271c2_map_layout = {
+ .main_pll = tda18271c2_main_pll,
+ .cal_pll = tda18271c2_cal_pll,
+
+ .rf_cal = tda18271c2_rf_cal,
+ .rf_cal_kmco = tda18271c2_km,
+
+ .rf_cal_dc_over_dt = tda18271_rf_cal_dc_over_dt,
+
+ .bp_filter = tda18271_bp_filter,
+ .rf_band = tda18271_rf_band,
+ .gain_taper = tda18271_gain_taper,
+ .ir_measure = tda18271_ir_measure,
+};
+
+int tda18271_assign_map_layout(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ int ret = 0;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ priv->maps = &tda18271c1_map_layout;
+ memcpy(&priv->std, &tda18271c1_std_map,
+ sizeof(struct tda18271_std_map));
+ break;
+ case TDA18271HDC2:
+ priv->maps = &tda18271c2_map_layout;
+ memcpy(&priv->std, &tda18271c2_std_map,
+ sizeof(struct tda18271_std_map));
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ memcpy(priv->rf_cal_state, &tda18271_rf_band_template,
+ sizeof(tda18271_rf_band_template));
+
+ return ret;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/tda18271.h b/drivers/media/dvb/frontends/tda18271.h
new file mode 100644
index 000000000000..24b0e35a2ab3
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda18271.h
@@ -0,0 +1,78 @@
+/*
+ tda18271.h - header for the Philips / NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, 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 __TDA18271_H__
+#define __TDA18271_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+struct tda18271_std_map_item {
+ u16 if_freq;
+ u8 std_bits;
+};
+
+struct tda18271_std_map {
+ struct tda18271_std_map_item fm_radio;
+ struct tda18271_std_map_item atv_b;
+ struct tda18271_std_map_item atv_dk;
+ struct tda18271_std_map_item atv_gh;
+ struct tda18271_std_map_item atv_i;
+ struct tda18271_std_map_item atv_l;
+ struct tda18271_std_map_item atv_lc;
+ struct tda18271_std_map_item atv_mn;
+ struct tda18271_std_map_item atsc_6;
+ struct tda18271_std_map_item dvbt_6;
+ struct tda18271_std_map_item dvbt_7;
+ struct tda18271_std_map_item dvbt_8;
+ struct tda18271_std_map_item qam_6;
+ struct tda18271_std_map_item qam_8;
+};
+
+enum tda18271_i2c_gate {
+ TDA18271_GATE_AUTO = 0,
+ TDA18271_GATE_ANALOG,
+ TDA18271_GATE_DIGITAL,
+};
+
+struct tda18271_config {
+ /* override default if freq / std settings (optional) */
+ struct tda18271_std_map *std_map;
+
+ /* use i2c gate provided by analog or digital demod */
+ enum tda18271_i2c_gate gate;
+};
+
+#if defined(CONFIG_DVB_TDA18271) || (defined(CONFIG_DVB_TDA18271_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
+ struct i2c_adapter *i2c,
+ struct tda18271_config *cfg);
+#else
+static inline struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe,
+ u8 addr,
+ struct i2c_adapter *i2c,
+ struct tda18271_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif
+
+#endif /* __TDA18271_H__ */
diff --git a/drivers/media/dvb/frontends/tda827x.c b/drivers/media/dvb/frontends/tda827x.c
index 256fc4bf500b..229b11987a58 100644
--- a/drivers/media/dvb/frontends/tda827x.c
+++ b/drivers/media/dvb/frontends/tda827x.c
@@ -19,12 +19,16 @@
*/
#include <linux/module.h>
-#include <linux/dvb/frontend.h>
#include <asm/types.h>
+#include <linux/dvb/frontend.h>
+#include <linux/videodev2.h>
#include "tda827x.h"
static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
#define dprintk(args...) \
do { \
if (debug) printk(KERN_DEBUG "tda827x: " args); \
@@ -34,10 +38,57 @@ struct tda827x_priv {
int i2c_addr;
struct i2c_adapter *i2c_adap;
struct tda827x_config *cfg;
+
+ unsigned int sgIF;
+ unsigned char lpsel;
+
u32 frequency;
u32 bandwidth;
};
+static void tda827x_set_std(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ char *mode;
+
+ priv->lpsel = 0;
+ if (params->std & V4L2_STD_MN) {
+ priv->sgIF = 92;
+ priv->lpsel = 1;
+ mode = "MN";
+ } else if (params->std & V4L2_STD_B) {
+ priv->sgIF = 108;
+ mode = "B";
+ } else if (params->std & V4L2_STD_GH) {
+ priv->sgIF = 124;
+ mode = "GH";
+ } else if (params->std & V4L2_STD_PAL_I) {
+ priv->sgIF = 124;
+ mode = "I";
+ } else if (params->std & V4L2_STD_DK) {
+ priv->sgIF = 124;
+ mode = "DK";
+ } else if (params->std & V4L2_STD_SECAM_L) {
+ priv->sgIF = 124;
+ mode = "L";
+ } else if (params->std & V4L2_STD_SECAM_LC) {
+ priv->sgIF = 20;
+ mode = "LC";
+ } else {
+ priv->sgIF = 124;
+ mode = "xx";
+ }
+
+ if (params->mode == V4L2_TUNER_RADIO)
+ priv->sgIF = 88; /* if frequency is 5.5 MHz */
+
+ dprintk("setting tda827x to system %s\n", mode);
+}
+
+
+/* ------------------------------------------------------------------ */
+
struct tda827x_data {
u32 lomax;
u8 spd;
@@ -48,7 +99,7 @@ struct tda827x_data {
u8 div1p5;
};
-static const struct tda827x_data tda827x_dvbt[] = {
+static const struct tda827x_data tda827x_table[] = {
{ .lomax = 62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
{ .lomax = 66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1},
{ .lomax = 76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0},
@@ -106,21 +157,22 @@ static int tda827xo_set_params(struct dvb_frontend *fe,
tuner_freq = params->frequency + if_freq;
i = 0;
- while (tda827x_dvbt[i].lomax < tuner_freq) {
- if(tda827x_dvbt[i + 1].lomax == 0)
+ while (tda827x_table[i].lomax < tuner_freq) {
+ if (tda827x_table[i + 1].lomax == 0)
break;
i++;
}
- N = ((tuner_freq + 125000) / 250000) << (tda827x_dvbt[i].spd + 2);
+ N = ((tuner_freq + 125000) / 250000) << (tda827x_table[i].spd + 2);
buf[0] = 0;
buf[1] = (N>>8) | 0x40;
buf[2] = N & 0xff;
buf[3] = 0;
buf[4] = 0x52;
- buf[5] = (tda827x_dvbt[i].spd << 6) + (tda827x_dvbt[i].div1p5 << 5) +
- (tda827x_dvbt[i].bs << 3) + tda827x_dvbt[i].bp;
- buf[6] = (tda827x_dvbt[i].gc3 << 4) + 0x8f;
+ buf[5] = (tda827x_table[i].spd << 6) + (tda827x_table[i].div1p5 << 5) +
+ (tda827x_table[i].bs << 3) +
+ tda827x_table[i].bp;
+ buf[6] = (tda827x_table[i].gc3 << 4) + 0x8f;
buf[7] = 0xbf;
buf[8] = 0x2a;
buf[9] = 0x05;
@@ -140,7 +192,7 @@ static int tda827xo_set_params(struct dvb_frontend *fe,
msleep(500);
/* correct CP value */
buf[0] = 0x30;
- buf[1] = 0x50 + tda827x_dvbt[i].cp;
+ buf[1] = 0x50 + tda827x_table[i].cp;
msg.len = 2;
if (fe->ops.i2c_gate_ctrl)
@@ -173,6 +225,102 @@ static int tda827xo_sleep(struct dvb_frontend *fe)
/* ------------------------------------------------------------------ */
+static int tda827xo_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ unsigned char tuner_reg[8];
+ unsigned char reg2[2];
+ u32 N;
+ int i;
+ struct tda827x_priv *priv = fe->tuner_priv;
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0 };
+ unsigned int freq = params->frequency;
+
+ tda827x_set_std(fe, params);
+
+ if (params->mode == V4L2_TUNER_RADIO)
+ freq = freq / 1000;
+
+ N = freq + priv->sgIF;
+
+ i = 0;
+ while (tda827x_table[i].lomax < N * 62500) {
+ if (tda827x_table[i + 1].lomax == 0)
+ break;
+ i++;
+ }
+
+ N = N << tda827x_table[i].spd;
+
+ tuner_reg[0] = 0;
+ tuner_reg[1] = (unsigned char)(N>>8);
+ tuner_reg[2] = (unsigned char) N;
+ tuner_reg[3] = 0x40;
+ tuner_reg[4] = 0x52 + (priv->lpsel << 5);
+ tuner_reg[5] = (tda827x_table[i].spd << 6) +
+ (tda827x_table[i].div1p5 << 5) +
+ (tda827x_table[i].bs << 3) + tda827x_table[i].bp;
+ tuner_reg[6] = 0x8f + (tda827x_table[i].gc3 << 4);
+ tuner_reg[7] = 0x8f;
+
+ msg.buf = tuner_reg;
+ msg.len = 8;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msg.buf = reg2;
+ msg.len = 2;
+ reg2[0] = 0x80;
+ reg2[1] = 0;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ reg2[0] = 0x60;
+ reg2[1] = 0xbf;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ reg2[0] = 0x30;
+ reg2[1] = tuner_reg[4] + 0x80;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msleep(1);
+ reg2[0] = 0x30;
+ reg2[1] = tuner_reg[4] + 4;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msleep(1);
+ reg2[0] = 0x30;
+ reg2[1] = tuner_reg[4];
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msleep(550);
+ reg2[0] = 0x30;
+ reg2[1] = (tuner_reg[4] & 0xfc) + tda827x_table[i].cp;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ reg2[0] = 0x60;
+ reg2[1] = 0x3f;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ reg2[0] = 0x80;
+ reg2[1] = 0x08; /* Vsync en */
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ priv->frequency = freq * 62500;
+
+ return 0;
+}
+
+static void tda827xo_agcf(struct dvb_frontend *fe)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ unsigned char data[] = { 0x80, 0x0c };
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = data, .len = 2};
+
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+}
+
+/* ------------------------------------------------------------------ */
+
struct tda827xa_data {
u32 lomax;
u8 svco;
@@ -212,6 +360,35 @@ static const struct tda827xa_data tda827xa_dvbt[] = {
{ .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}
};
+static struct tda827xa_data tda827xa_analog[] = {
+ { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 3},
+ { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3},
+ { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3},
+ { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3},
+ { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1},
+ { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1},
+ { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 3},
+ { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 3},
+ { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1},
+ { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3},
+ { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3},
+ { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
+ { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1},
+ { .lomax = 554000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1},
+ { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
+ { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
+ { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
+ { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
+ { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0},
+ { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0},
+ { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0},
+ { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}
+};
+
static int tda827xa_set_params(struct dvb_frontend *fe,
struct dvb_frontend_parameters *params)
{
@@ -368,6 +545,163 @@ static int tda827xa_sleep(struct dvb_frontend *fe)
return 0;
}
+/* ------------------------------------------------------------------ */
+
+static void tda827xa_lna_gain(struct dvb_frontend *fe, int high,
+ struct analog_parameters *params)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ unsigned char buf[] = {0x22, 0x01};
+ int arg;
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
+
+ if (NULL == priv->cfg) {
+ dprintk("tda827x_config not defined, cannot set LNA gain!\n");
+ return;
+ }
+
+ if (priv->cfg->config) {
+ if (high)
+ dprintk("setting LNA to high gain\n");
+ else
+ dprintk("setting LNA to low gain\n");
+ }
+ switch (*priv->cfg->config) {
+ case 0: /* no LNA */
+ break;
+ case 1: /* switch is GPIO 0 of tda8290 */
+ case 2:
+ /* turn Vsync on */
+ if (params->std & V4L2_STD_MN)
+ arg = 1;
+ else
+ arg = 0;
+ if (priv->cfg->tuner_callback)
+ priv->cfg->tuner_callback(priv->i2c_adap->algo_data,
+ 1, arg);
+ buf[1] = high ? 0 : 1;
+ if (*priv->cfg->config == 2)
+ buf[1] = high ? 1 : 0;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+ break;
+ case 3: /* switch with GPIO of saa713x */
+ if (priv->cfg->tuner_callback)
+ priv->cfg->tuner_callback(priv->i2c_adap->algo_data,
+ 0, high);
+ break;
+ }
+}
+
+static int tda827xa_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ unsigned char tuner_reg[11];
+ u32 N;
+ int i;
+ struct tda827x_priv *priv = fe->tuner_priv;
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = tuner_reg, .len = sizeof(tuner_reg) };
+ unsigned int freq = params->frequency;
+
+ tda827x_set_std(fe, params);
+
+ tda827xa_lna_gain(fe, 1, params);
+ msleep(10);
+
+ if (params->mode == V4L2_TUNER_RADIO)
+ freq = freq / 1000;
+
+ N = freq + priv->sgIF;
+
+ i = 0;
+ while (tda827xa_analog[i].lomax < N * 62500) {
+ if (tda827xa_analog[i + 1].lomax == 0)
+ break;
+ i++;
+ }
+
+ N = N << tda827xa_analog[i].spd;
+
+ tuner_reg[0] = 0;
+ tuner_reg[1] = (unsigned char)(N>>8);
+ tuner_reg[2] = (unsigned char) N;
+ tuner_reg[3] = 0;
+ tuner_reg[4] = 0x16;
+ tuner_reg[5] = (tda827xa_analog[i].spd << 5) +
+ (tda827xa_analog[i].svco << 3) +
+ tda827xa_analog[i].sbs;
+ tuner_reg[6] = 0x8b + (tda827xa_analog[i].gc3 << 4);
+ tuner_reg[7] = 0x1c;
+ tuner_reg[8] = 4;
+ tuner_reg[9] = 0x20;
+ tuner_reg[10] = 0x00;
+ msg.len = 11;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tuner_reg[0] = 0x90;
+ tuner_reg[1] = 0xff;
+ tuner_reg[2] = 0xe0;
+ tuner_reg[3] = 0;
+ tuner_reg[4] = 0x99 + (priv->lpsel << 1);
+ msg.len = 5;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tuner_reg[0] = 0xa0;
+ tuner_reg[1] = 0xc0;
+ msg.len = 2;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tuner_reg[0] = 0x30;
+ tuner_reg[1] = 0x10 + tda827xa_analog[i].scr;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msg.flags = I2C_M_RD;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+ msg.flags = 0;
+ tuner_reg[1] >>= 4;
+ dprintk("AGC2 gain is: %d\n", tuner_reg[1]);
+ if (tuner_reg[1] < 1)
+ tda827xa_lna_gain(fe, 0, params);
+
+ msleep(100);
+ tuner_reg[0] = 0x60;
+ tuner_reg[1] = 0x3c;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ msleep(163);
+ tuner_reg[0] = 0x50;
+ tuner_reg[1] = 0x8f + (tda827xa_analog[i].gc3 << 4);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tuner_reg[0] = 0x80;
+ tuner_reg[1] = 0x28;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tuner_reg[0] = 0xb0;
+ tuner_reg[1] = 0x01;
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ tuner_reg[0] = 0xc0;
+ tuner_reg[1] = 0x19 + (priv->lpsel << 1);
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+
+ priv->frequency = freq * 62500;
+
+ return 0;
+}
+
+static void tda827xa_agcf(struct dvb_frontend *fe)
+{
+ struct tda827x_priv *priv = fe->tuner_priv;
+ unsigned char data[] = {0x80, 0x2c};
+ struct i2c_msg msg = {.addr = priv->i2c_addr, .flags = 0,
+ .buf = data, .len = 2};
+ i2c_transfer(priv->i2c_adap, &msg, 1);
+}
+
+/* ------------------------------------------------------------------ */
+
static int tda827x_release(struct dvb_frontend *fe)
{
kfree(fe->tuner_priv);
@@ -430,6 +764,7 @@ static struct dvb_tuner_ops tda827xo_tuner_ops = {
.init = tda827x_initial_init,
.sleep = tda827x_initial_sleep,
.set_params = tda827xo_set_params,
+ .set_analog_params = tda827xo_set_analog_params,
.get_frequency = tda827x_get_frequency,
.get_bandwidth = tda827x_get_bandwidth,
};
@@ -445,6 +780,7 @@ static struct dvb_tuner_ops tda827xa_tuner_ops = {
.init = tda827x_init,
.sleep = tda827xa_sleep,
.set_params = tda827xa_set_params,
+ .set_analog_params = tda827xa_set_analog_params,
.get_frequency = tda827x_get_frequency,
.get_bandwidth = tda827x_get_bandwidth,
};
@@ -465,9 +801,13 @@ static int tda827x_probe_version(struct dvb_frontend *fe)
dprintk("tda827x tuner found\n");
fe->ops.tuner_ops.init = tda827x_init;
fe->ops.tuner_ops.sleep = tda827xo_sleep;
+ if (priv->cfg)
+ priv->cfg->agcf = tda827xo_agcf;
} else {
dprintk("tda827xa tuner found\n");
memcpy(&fe->ops.tuner_ops, &tda827xa_tuner_ops, sizeof(struct dvb_tuner_ops));
+ if (priv->cfg)
+ priv->cfg->agcf = tda827xa_agcf;
}
return 0;
}
@@ -487,16 +827,13 @@ struct dvb_frontend *tda827x_attach(struct dvb_frontend *fe, int addr,
priv->i2c_adap = i2c;
priv->cfg = cfg;
memcpy(&fe->ops.tuner_ops, &tda827xo_tuner_ops, sizeof(struct dvb_tuner_ops));
-
fe->tuner_priv = priv;
+ dprintk("type set to %s\n", fe->ops.tuner_ops.info.name);
+
return fe;
}
-
-EXPORT_SYMBOL(tda827x_attach);
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+EXPORT_SYMBOL_GPL(tda827x_attach);
MODULE_DESCRIPTION("DVB TDA827x driver");
MODULE_AUTHOR("Hartmut Hackmann <hartmut.hackmann@t-online.de>");
diff --git a/drivers/media/dvb/frontends/tda827x.h b/drivers/media/dvb/frontends/tda827x.h
index 69e8263d6d59..92eb65b4012b 100644
--- a/drivers/media/dvb/frontends/tda827x.h
+++ b/drivers/media/dvb/frontends/tda827x.h
@@ -29,9 +29,16 @@
struct tda827x_config
{
+ /* saa7134 - provided callbacks */
void (*lna_gain) (struct dvb_frontend *fe, int high);
int (*init) (struct dvb_frontend *fe);
int (*sleep) (struct dvb_frontend *fe);
+
+ /* interface to tda829x driver */
+ unsigned int *config;
+ int (*tuner_callback) (void *dev, int command, int arg);
+
+ void (*agcf)(struct dvb_frontend *fe);
};
diff --git a/drivers/media/dvb/frontends/ves1820.c b/drivers/media/dvb/frontends/ves1820.c
index 60433b5011fd..8791701c8f25 100644
--- a/drivers/media/dvb/frontends/ves1820.c
+++ b/drivers/media/dvb/frontends/ves1820.c
@@ -65,7 +65,7 @@ static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data)
ret = i2c_transfer(state->i2c, &msg, 1);
if (ret != 1)
- printk("ves1820: %s(): writereg error (reg == 0x%02x,"
+ printk("ves1820: %s(): writereg error (reg == 0x%02x, "
"val == 0x%02x, ret == %i)\n", __FUNCTION__, reg, data, ret);
return (ret != 1) ? -EREMOTEIO : 0;
@@ -84,7 +84,7 @@ static u8 ves1820_readreg(struct ves1820_state *state, u8 reg)
ret = i2c_transfer(state->i2c, msg, 2);
if (ret != 2)
- printk("ves1820: %s(): readreg error (reg == 0x%02x,"
+ printk("ves1820: %s(): readreg error (reg == 0x%02x, "
"ret == %i)\n", __FUNCTION__, reg, ret);
return b1[0];
diff --git a/drivers/media/dvb/frontends/xc5000.c b/drivers/media/dvb/frontends/xc5000.c
new file mode 100644
index 000000000000..f642ca200b59
--- /dev/null
+++ b/drivers/media/dvb/frontends/xc5000.c
@@ -0,0 +1,964 @@
+/*
+ * Driver for Xceive XC5000 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2007 Xceive Corporation
+ * Copyright (c) 2007 Steven Toth <stoth@hauppauge.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "xc5000.h"
+#include "xc5000_priv.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_INFO "%s: " fmt, "xc5000", ## arg)
+
+#define XC5000_DEFAULT_FIRMWARE "dvb-fe-xc5000-1.1.fw"
+#define XC5000_DEFAULT_FIRMWARE_SIZE 12332
+
+/* Misc Defines */
+#define MAX_TV_STANDARD 23
+#define XC_MAX_I2C_WRITE_LENGTH 64
+
+/* Signal Types */
+#define XC_RF_MODE_AIR 0
+#define XC_RF_MODE_CABLE 1
+
+/* Result codes */
+#define XC_RESULT_SUCCESS 0
+#define XC_RESULT_RESET_FAILURE 1
+#define XC_RESULT_I2C_WRITE_FAILURE 2
+#define XC_RESULT_I2C_READ_FAILURE 3
+#define XC_RESULT_OUT_OF_RANGE 5
+
+/* Product id */
+#define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000
+#define XC_PRODUCT_ID_FW_LOADED 0x1388
+
+/* Registers */
+#define XREG_INIT 0x00
+#define XREG_VIDEO_MODE 0x01
+#define XREG_AUDIO_MODE 0x02
+#define XREG_RF_FREQ 0x03
+#define XREG_D_CODE 0x04
+#define XREG_IF_OUT 0x05
+#define XREG_SEEK_MODE 0x07
+#define XREG_POWER_DOWN 0x0A
+#define XREG_SIGNALSOURCE 0x0D /* 0=Air, 1=Cable */
+#define XREG_SMOOTHEDCVBS 0x0E
+#define XREG_XTALFREQ 0x0F
+#define XREG_FINERFFREQ 0x10
+#define XREG_DDIMODE 0x11
+
+#define XREG_ADC_ENV 0x00
+#define XREG_QUALITY 0x01
+#define XREG_FRAME_LINES 0x02
+#define XREG_HSYNC_FREQ 0x03
+#define XREG_LOCK 0x04
+#define XREG_FREQ_ERROR 0x05
+#define XREG_SNR 0x06
+#define XREG_VERSION 0x07
+#define XREG_PRODUCT_ID 0x08
+#define XREG_BUSY 0x09
+
+/*
+ Basic firmware description. This will remain with
+ the driver for documentation purposes.
+
+ This represents an I2C firmware file encoded as a
+ string of unsigned char. Format is as follows:
+
+ char[0 ]=len0_MSB -> len = len_MSB * 256 + len_LSB
+ char[1 ]=len0_LSB -> length of first write transaction
+ char[2 ]=data0 -> first byte to be sent
+ char[3 ]=data1
+ char[4 ]=data2
+ char[ ]=...
+ char[M ]=dataN -> last byte to be sent
+ char[M+1]=len1_MSB -> len = len_MSB * 256 + len_LSB
+ char[M+2]=len1_LSB -> length of second write transaction
+ char[M+3]=data0
+ char[M+4]=data1
+ ...
+ etc.
+
+ The [len] value should be interpreted as follows:
+
+ len= len_MSB _ len_LSB
+ len=1111_1111_1111_1111 : End of I2C_SEQUENCE
+ len=0000_0000_0000_0000 : Reset command: Do hardware reset
+ len=0NNN_NNNN_NNNN_NNNN : Normal transaction: number of bytes = {1:32767)
+ len=1WWW_WWWW_WWWW_WWWW : Wait command: wait for {1:32767} ms
+
+ For the RESET and WAIT commands, the two following bytes will contain
+ immediately the length of the following transaction.
+
+*/
+typedef struct {
+ char *Name;
+ u16 AudioMode;
+ u16 VideoMode;
+} XC_TV_STANDARD;
+
+/* Tuner standards */
+#define MN_NTSC_PAL_BTSC 0
+#define MN_NTSC_PAL_A2 1
+#define MN_NTSC_PAL_EIAJ 2
+#define MN_NTSC_PAL_Mono 3
+#define BG_PAL_A2 4
+#define BG_PAL_NICAM 5
+#define BG_PAL_MONO 6
+#define I_PAL_NICAM 7
+#define I_PAL_NICAM_MONO 8
+#define DK_PAL_A2 9
+#define DK_PAL_NICAM 10
+#define DK_PAL_MONO 11
+#define DK_SECAM_A2DK1 12
+#define DK_SECAM_A2LDK3 13
+#define DK_SECAM_A2MONO 14
+#define L_SECAM_NICAM 15
+#define LC_SECAM_NICAM 16
+#define DTV6 17
+#define DTV8 18
+#define DTV7_8 19
+#define DTV7 20
+#define FM_Radio_INPUT2 21
+#define FM_Radio_INPUT1 22
+
+XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
+ {"M/N-NTSC/PAL-BTSC", 0x0400, 0x8020},
+ {"M/N-NTSC/PAL-A2", 0x0600, 0x8020},
+ {"M/N-NTSC/PAL-EIAJ", 0x0440, 0x8020},
+ {"M/N-NTSC/PAL-Mono", 0x0478, 0x8020},
+ {"B/G-PAL-A2", 0x0A00, 0x8049},
+ {"B/G-PAL-NICAM", 0x0C04, 0x8049},
+ {"B/G-PAL-MONO", 0x0878, 0x8059},
+ {"I-PAL-NICAM", 0x1080, 0x8009},
+ {"I-PAL-NICAM-MONO", 0x0E78, 0x8009},
+ {"D/K-PAL-A2", 0x1600, 0x8009},
+ {"D/K-PAL-NICAM", 0x0E80, 0x8009},
+ {"D/K-PAL-MONO", 0x1478, 0x8009},
+ {"D/K-SECAM-A2 DK1", 0x1200, 0x8009},
+ {"D/K-SECAM-A2 L/DK3",0x0E00, 0x8009},
+ {"D/K-SECAM-A2 MONO", 0x1478, 0x8009},
+ {"L-SECAM-NICAM", 0x8E82, 0x0009},
+ {"L'-SECAM-NICAM", 0x8E82, 0x4009},
+ {"DTV6", 0x00C0, 0x8002},
+ {"DTV8", 0x00C0, 0x800B},
+ {"DTV7/8", 0x00C0, 0x801B},
+ {"DTV7", 0x00C0, 0x8007},
+ {"FM Radio-INPUT2", 0x9802, 0x9002},
+ {"FM Radio-INPUT1", 0x0208, 0x9002}
+};
+
+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);
+
+static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
+{
+ return xc5000_writeregs(priv, buf, len)
+ ? XC_RESULT_I2C_WRITE_FAILURE : XC_RESULT_SUCCESS;
+}
+
+static int xc_read_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
+{
+ return xc5000_readregs(priv, buf, len)
+ ? XC_RESULT_I2C_READ_FAILURE : XC_RESULT_SUCCESS;
+}
+
+static int xc_reset(struct dvb_frontend *fe)
+{
+ xc5000_TunerReset(fe);
+ return XC_RESULT_SUCCESS;
+}
+
+static void xc_wait(int wait_ms)
+{
+ msleep(wait_ms);
+}
+
+static void xc5000_TunerReset(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (priv->cfg->tuner_callback) {
+ ret = priv->cfg->tuner_callback(priv->cfg->priv,
+ XC5000_TUNER_RESET, 0);
+ if (ret)
+ printk(KERN_ERR "xc5000: reset failed\n");
+ } else
+ printk(KERN_ERR "xc5000: no tuner reset callback function, fatal\n");
+}
+
+static int xc_write_reg(struct xc5000_priv *priv, u16 regAddr, u16 i2cData)
+{
+ u8 buf[4];
+ int WatchDogTimer = 5;
+ int result;
+
+ buf[0] = (regAddr >> 8) & 0xFF;
+ buf[1] = regAddr & 0xFF;
+ buf[2] = (i2cData >> 8) & 0xFF;
+ buf[3] = i2cData & 0xFF;
+ result = xc_send_i2c_data(priv, buf, 4);
+ if (result == XC_RESULT_SUCCESS) {
+ /* wait for busy flag to clear */
+ while ((WatchDogTimer > 0) && (result == XC_RESULT_SUCCESS)) {
+ buf[0] = 0;
+ buf[1] = XREG_BUSY;
+
+ result = xc_send_i2c_data(priv, buf, 2);
+ if (result == XC_RESULT_SUCCESS) {
+ result = xc_read_i2c_data(priv, buf, 2);
+ if (result == XC_RESULT_SUCCESS) {
+ if ((buf[0] == 0) && (buf[1] == 0)) {
+ /* busy flag cleared */
+ break;
+ } else {
+ xc_wait(100); /* wait 5 ms */
+ WatchDogTimer--;
+ }
+ }
+ }
+ }
+ }
+ if (WatchDogTimer < 0)
+ result = XC_RESULT_I2C_WRITE_FAILURE;
+
+ return result;
+}
+
+static int xc_read_reg(struct xc5000_priv *priv, u16 regAddr, u16 *i2cData)
+{
+ u8 buf[2];
+ int result;
+
+ buf[0] = (regAddr >> 8) & 0xFF;
+ buf[1] = regAddr & 0xFF;
+ result = xc_send_i2c_data(priv, buf, 2);
+ if (result != XC_RESULT_SUCCESS)
+ return result;
+
+ result = xc_read_i2c_data(priv, buf, 2);
+ if (result != XC_RESULT_SUCCESS)
+ return result;
+
+ *i2cData = buf[0] * 256 + buf[1];
+ return result;
+}
+
+static int xc_load_i2c_sequence(struct dvb_frontend *fe, u8 i2c_sequence[])
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+
+ int i, nbytes_to_send, result;
+ unsigned int len, pos, index;
+ u8 buf[XC_MAX_I2C_WRITE_LENGTH];
+
+ index=0;
+ while ((i2c_sequence[index]!=0xFF) || (i2c_sequence[index+1]!=0xFF)) {
+ len = i2c_sequence[index]* 256 + i2c_sequence[index+1];
+ if (len == 0x0000) {
+ /* RESET command */
+ result = xc_reset(fe);
+ index += 2;
+ if (result != XC_RESULT_SUCCESS)
+ return result;
+ } else if (len & 0x8000) {
+ /* WAIT command */
+ xc_wait(len & 0x7FFF);
+ index += 2;
+ } else {
+ /* Send i2c data whilst ensuring individual transactions
+ * do not exceed XC_MAX_I2C_WRITE_LENGTH bytes.
+ */
+ index += 2;
+ buf[0] = i2c_sequence[index];
+ buf[1] = i2c_sequence[index + 1];
+ pos = 2;
+ while (pos < len) {
+ if ((len - pos) > XC_MAX_I2C_WRITE_LENGTH - 2) {
+ nbytes_to_send = XC_MAX_I2C_WRITE_LENGTH;
+ } else {
+ nbytes_to_send = (len - pos + 2);
+ }
+ for (i=2; i<nbytes_to_send; i++) {
+ buf[i] = i2c_sequence[index + pos + i - 2];
+ }
+ result = xc_send_i2c_data(priv, buf, nbytes_to_send);
+
+ if (result != XC_RESULT_SUCCESS)
+ return result;
+
+ pos += nbytes_to_send - 2;
+ }
+ index += len;
+ }
+ }
+ return XC_RESULT_SUCCESS;
+}
+
+static int xc_initialize(struct xc5000_priv *priv)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ return xc_write_reg(priv, XREG_INIT, 0);
+}
+
+static int xc_SetTVStandard(struct xc5000_priv *priv,
+ u16 VideoMode, u16 AudioMode)
+{
+ int ret;
+ dprintk(1, "%s(0x%04x,0x%04x)\n", __FUNCTION__, VideoMode, AudioMode);
+ dprintk(1, "%s() Standard = %s\n",
+ __FUNCTION__,
+ XC5000_Standard[priv->video_standard].Name);
+
+ ret = xc_write_reg(priv, XREG_VIDEO_MODE, VideoMode);
+ if (ret == XC_RESULT_SUCCESS)
+ ret = xc_write_reg(priv, XREG_AUDIO_MODE, AudioMode);
+
+ return ret;
+}
+
+static int xc_shutdown(struct xc5000_priv *priv)
+{
+ return 0;
+ /* Fixme: cannot bring tuner back alive once shutdown
+ * without reloading the driver modules.
+ * return xc_write_reg(priv, XREG_POWER_DOWN, 0);
+ */
+}
+
+static int xc_SetSignalSource(struct xc5000_priv *priv, u16 rf_mode)
+{
+ dprintk(1, "%s(%d) Source = %s\n", __FUNCTION__, rf_mode,
+ rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE");
+
+ if ((rf_mode != XC_RF_MODE_AIR) && (rf_mode != XC_RF_MODE_CABLE))
+ {
+ rf_mode = XC_RF_MODE_CABLE;
+ printk(KERN_ERR
+ "%s(), Invalid mode, defaulting to CABLE",
+ __FUNCTION__);
+ }
+ return xc_write_reg(priv, XREG_SIGNALSOURCE, rf_mode);
+}
+
+static const struct dvb_tuner_ops xc5000_tuner_ops;
+
+static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz)
+{
+ u16 freq_code;
+
+ dprintk(1, "%s(%u)\n", __FUNCTION__, freq_hz);
+
+ if ((freq_hz > xc5000_tuner_ops.info.frequency_max) ||
+ (freq_hz < xc5000_tuner_ops.info.frequency_min))
+ return XC_RESULT_OUT_OF_RANGE;
+
+ freq_code = (u16)(freq_hz / 15625);
+
+ return xc_write_reg(priv, XREG_RF_FREQ, freq_code);
+}
+
+
+static int xc_set_IF_frequency(struct xc5000_priv *priv, u32 freq_khz)
+{
+ u32 freq_code = (freq_khz * 1024)/1000;
+ dprintk(1, "%s(freq_khz = %d) freq_code = 0x%x\n",
+ __FUNCTION__, freq_khz, freq_code);
+
+ return xc_write_reg(priv, XREG_IF_OUT, freq_code);
+}
+
+
+static int xc_get_ADC_Envelope(struct xc5000_priv *priv, u16 *adc_envelope)
+{
+ return xc_read_reg(priv, XREG_ADC_ENV, adc_envelope);
+}
+
+static int xc_get_frequency_error(struct xc5000_priv *priv, u32 *freq_error_hz)
+{
+ int result;
+ u16 regData;
+ u32 tmp;
+
+ result = xc_read_reg(priv, XREG_FREQ_ERROR, &regData);
+ if (result)
+ return result;
+
+ tmp = (u32)regData;
+ (*freq_error_hz) = (tmp * 15625) / 1000;
+ return result;
+}
+
+static int xc_get_lock_status(struct xc5000_priv *priv, u16 *lock_status)
+{
+ return xc_read_reg(priv, XREG_LOCK, lock_status);
+}
+
+static int xc_get_version(struct xc5000_priv *priv,
+ u8 *hw_majorversion, u8 *hw_minorversion,
+ u8 *fw_majorversion, u8 *fw_minorversion)
+{
+ u16 data;
+ int result;
+
+ result = xc_read_reg(priv, XREG_VERSION, &data);
+ if (result)
+ return result;
+
+ (*hw_majorversion) = (data >> 12) & 0x0F;
+ (*hw_minorversion) = (data >> 8) & 0x0F;
+ (*fw_majorversion) = (data >> 4) & 0x0F;
+ (*fw_minorversion) = data & 0x0F;
+
+ return 0;
+}
+
+static int xc_get_hsync_freq(struct xc5000_priv *priv, u32 *hsync_freq_hz)
+{
+ u16 regData;
+ int result;
+
+ result = xc_read_reg(priv, XREG_HSYNC_FREQ, &regData);
+ if (result)
+ return result;
+
+ (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100;
+ return result;
+}
+
+static int xc_get_frame_lines(struct xc5000_priv *priv, u16 *frame_lines)
+{
+ return xc_read_reg(priv, XREG_FRAME_LINES, frame_lines);
+}
+
+static int xc_get_quality(struct xc5000_priv *priv, u16 *quality)
+{
+ return xc_read_reg(priv, XREG_QUALITY, quality);
+}
+
+static u16 WaitForLock(struct xc5000_priv *priv)
+{
+ u16 lockState = 0;
+ int watchDogCount = 40;
+
+ while ((lockState == 0) && (watchDogCount > 0)) {
+ xc_get_lock_status(priv, &lockState);
+ if (lockState != 1) {
+ xc_wait(5);
+ watchDogCount--;
+ }
+ }
+ return lockState;
+}
+
+static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz)
+{
+ int found = 0;
+
+ dprintk(1, "%s(%u)\n", __FUNCTION__, freq_hz);
+
+ if (xc_set_RF_frequency(priv, freq_hz) != XC_RESULT_SUCCESS)
+ return 0;
+
+ if (WaitForLock(priv) == 1)
+ found = 1;
+
+ return found;
+}
+
+static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val)
+{
+ u8 buf[2] = { reg >> 8, reg & 0xff };
+ u8 bval[2] = { 0, 0 };
+ struct i2c_msg msg[2] = {
+ { .addr = priv->cfg->i2c_address,
+ .flags = 0, .buf = &buf[0], .len = 2 },
+ { .addr = priv->cfg->i2c_address,
+ .flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
+ };
+
+ if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+ printk(KERN_WARNING "xc5000: I2C read failed\n");
+ return -EREMOTEIO;
+ }
+
+ *val = (bval[0] << 8) | bval[1];
+ return 0;
+}
+
+static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len)
+{
+ struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
+ .flags = 0, .buf = buf, .len = len };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n",
+ (int)len);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int xc5000_readregs(struct xc5000_priv *priv, u8 *buf, u8 len)
+{
+ struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
+ .flags = I2C_M_RD, .buf = buf, .len = len };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_ERR "xc5000 I2C read failed (len=%i)\n",(int)len);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int xc5000_fwupload(struct dvb_frontend* fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ const struct firmware *fw;
+ int ret;
+
+ /* request the firmware, this will block and timeout */
+ printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
+ XC5000_DEFAULT_FIRMWARE);
+
+ ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE, &priv->i2c->dev);
+ if (ret) {
+ printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
+ ret = XC_RESULT_RESET_FAILURE;
+ goto out;
+ } else {
+ printk(KERN_INFO "xc5000: firmware read %Zu bytes.\n",
+ fw->size);
+ ret = XC_RESULT_SUCCESS;
+ }
+
+ if (fw->size != XC5000_DEFAULT_FIRMWARE_SIZE) {
+ printk(KERN_ERR "xc5000: firmware incorrect size\n");
+ ret = XC_RESULT_RESET_FAILURE;
+ } else {
+ printk(KERN_INFO "xc5000: firmware upload\n");
+ ret = xc_load_i2c_sequence(fe, fw->data );
+ }
+
+out:
+ release_firmware(fw);
+ return ret;
+}
+
+static void xc_debug_dump(struct xc5000_priv *priv)
+{
+ u16 adc_envelope;
+ u32 freq_error_hz = 0;
+ u16 lock_status;
+ u32 hsync_freq_hz = 0;
+ u16 frame_lines;
+ u16 quality;
+ u8 hw_majorversion = 0, hw_minorversion = 0;
+ u8 fw_majorversion = 0, fw_minorversion = 0;
+
+ /* Wait for stats to stabilize.
+ * Frame Lines needs two frame times after initial lock
+ * before it is valid.
+ */
+ xc_wait(100);
+
+ xc_get_ADC_Envelope(priv, &adc_envelope);
+ dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope);
+
+ xc_get_frequency_error(priv, &freq_error_hz);
+ dprintk(1, "*** Frequency error = %d Hz\n", freq_error_hz);
+
+ xc_get_lock_status(priv, &lock_status);
+ dprintk(1, "*** Lock status (0-Wait, 1-Locked, 2-No-signal) = %d\n",
+ lock_status);
+
+ xc_get_version(priv, &hw_majorversion, &hw_minorversion,
+ &fw_majorversion, &fw_minorversion);
+ dprintk(1, "*** HW: V%02x.%02x, FW: V%02x.%02x\n",
+ hw_majorversion, hw_minorversion,
+ fw_majorversion, fw_minorversion);
+
+ xc_get_hsync_freq(priv, &hsync_freq_hz);
+ dprintk(1, "*** Horizontal sync frequency = %d Hz\n", hsync_freq_hz);
+
+ xc_get_frame_lines(priv, &frame_lines);
+ dprintk(1, "*** Frame lines = %d\n", frame_lines);
+
+ xc_get_quality(priv, &quality);
+ dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality);
+}
+
+static int xc5000_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dprintk(1, "%s() frequency=%d (Hz)\n", __FUNCTION__, params->frequency);
+
+ switch(params->u.vsb.modulation) {
+ case VSB_8:
+ case VSB_16:
+ dprintk(1, "%s() VSB modulation\n", __FUNCTION__);
+ priv->rf_mode = XC_RF_MODE_AIR;
+ priv->freq_hz = params->frequency - 1750000;
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ priv->video_standard = DTV6;
+ break;
+ case QAM_64:
+ case QAM_256:
+ case QAM_AUTO:
+ dprintk(1, "%s() QAM modulation\n", __FUNCTION__);
+ priv->rf_mode = XC_RF_MODE_CABLE;
+ priv->freq_hz = params->frequency - 1750000;
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ priv->video_standard = DTV6;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dprintk(1, "%s() frequency=%d (compensated)\n",
+ __FUNCTION__, priv->freq_hz);
+
+ ret = xc_SetSignalSource(priv, priv->rf_mode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR
+ "xc5000: xc_SetSignalSource(%d) failed\n",
+ priv->rf_mode);
+ return -EREMOTEIO;
+ }
+
+ ret = xc_SetTVStandard(priv,
+ XC5000_Standard[priv->video_standard].VideoMode,
+ XC5000_Standard[priv->video_standard].AudioMode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+ return -EREMOTEIO;
+ }
+
+ ret = xc_set_IF_frequency(priv, priv->cfg->if_khz);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n",
+ priv->cfg->if_khz);
+ return -EIO;
+ }
+
+ xc_tune_channel(priv, priv->freq_hz);
+
+ if (debug)
+ xc_debug_dump(priv);
+
+ return 0;
+}
+
+static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe);
+
+static int xc5000_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ if(priv->fwloaded == 0)
+ xc_load_fw_and_init_tuner(fe);
+
+ dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
+ __FUNCTION__, params->frequency);
+
+ priv->rf_mode = XC_RF_MODE_CABLE; /* Fix me: it could be air. */
+
+ /* params->frequency is in units of 62.5khz */
+ priv->freq_hz = params->frequency * 62500;
+
+ /* FIX ME: Some video standards may have several possible audio
+ standards. We simply default to one of them here.
+ */
+ if(params->std & V4L2_STD_MN) {
+ /* default to BTSC audio standard */
+ priv->video_standard = MN_NTSC_PAL_BTSC;
+ goto tune_channel;
+ }
+
+ if(params->std & V4L2_STD_PAL_BG) {
+ /* default to NICAM audio standard */
+ priv->video_standard = BG_PAL_NICAM;
+ goto tune_channel;
+ }
+
+ if(params->std & V4L2_STD_PAL_I) {
+ /* default to NICAM audio standard */
+ priv->video_standard = I_PAL_NICAM;
+ goto tune_channel;
+ }
+
+ if(params->std & V4L2_STD_PAL_DK) {
+ /* default to NICAM audio standard */
+ priv->video_standard = DK_PAL_NICAM;
+ goto tune_channel;
+ }
+
+ if(params->std & V4L2_STD_SECAM_DK) {
+ /* default to A2 DK1 audio standard */
+ priv->video_standard = DK_SECAM_A2DK1;
+ goto tune_channel;
+ }
+
+ if(params->std & V4L2_STD_SECAM_L) {
+ priv->video_standard = L_SECAM_NICAM;
+ goto tune_channel;
+ }
+
+ if(params->std & V4L2_STD_SECAM_LC) {
+ priv->video_standard = LC_SECAM_NICAM;
+ goto tune_channel;
+ }
+
+tune_channel:
+ ret = xc_SetSignalSource(priv, priv->rf_mode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR
+ "xc5000: xc_SetSignalSource(%d) failed\n",
+ priv->rf_mode);
+ return -EREMOTEIO;
+ }
+
+ ret = xc_SetTVStandard(priv,
+ XC5000_Standard[priv->video_standard].VideoMode,
+ XC5000_Standard[priv->video_standard].AudioMode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+ return -EREMOTEIO;
+ }
+
+ xc_tune_channel(priv, priv->freq_hz);
+
+ if (debug)
+ xc_debug_dump(priv);
+
+ return 0;
+}
+
+static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __FUNCTION__);
+ *freq = priv->freq_hz;
+ return 0;
+}
+
+static int xc5000_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ *bw = priv->bandwidth;
+ return 0;
+}
+
+static int xc5000_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ u16 lock_status = 0;
+
+ xc_get_lock_status(priv, &lock_status);
+
+ dprintk(1, "%s() lock_status = 0x%08x\n", __FUNCTION__, lock_status);
+
+ *status = lock_status;
+
+ return 0;
+}
+
+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) {
+ ret = xc5000_fwupload(fe);
+ if (ret != XC_RESULT_SUCCESS)
+ return ret;
+ priv->fwloaded = 1;
+ }
+
+ /* Start the tuner self-calibration process */
+ ret |= xc_initialize(priv);
+
+ /* Wait for calibration to complete.
+ * We could continue but XC5000 will clock stretch subsequent
+ * I2C transactions until calibration is complete. This way we
+ * don't have to rely on clock stretching working.
+ */
+ xc_wait( 100 );
+
+ /* Default to "CABLE" mode */
+ ret |= xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE);
+
+ return ret;
+}
+
+static int xc5000_sleep(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ /* On Pinnacle PCTV HD 800i, the tuner cannot be reinitialized
+ * once shutdown without reloading the driver. Maybe I am not
+ * doing something right.
+ *
+ */
+
+ ret = xc_shutdown(priv);
+ if(ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR
+ "xc5000: %s() unable to shutdown tuner\n",
+ __FUNCTION__);
+ return -EREMOTEIO;
+ }
+ else {
+ /* priv->fwloaded = 0; */
+ return XC_RESULT_SUCCESS;
+ }
+}
+
+static int xc5000_init(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: Unable to initialise tuner\n");
+ return -EREMOTEIO;
+ }
+
+ if (debug)
+ xc_debug_dump(priv);
+
+ return 0;
+}
+
+static int xc5000_release(struct dvb_frontend *fe)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static const struct dvb_tuner_ops xc5000_tuner_ops = {
+ .info = {
+ .name = "Xceive XC5000",
+ .frequency_min = 1000000,
+ .frequency_max = 1023000000,
+ .frequency_step = 50000,
+ },
+
+ .release = xc5000_release,
+ .init = xc5000_init,
+ .sleep = xc5000_sleep,
+
+ .set_params = xc5000_set_params,
+ .set_analog_params = xc5000_set_analog_params,
+ .get_frequency = xc5000_get_frequency,
+ .get_bandwidth = xc5000_get_bandwidth,
+ .get_status = xc5000_get_status
+};
+
+struct dvb_frontend * xc5000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct xc5000_config *cfg)
+{
+ struct xc5000_priv *priv = NULL;
+ u16 id = 0;
+
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ priv = kzalloc(sizeof(struct xc5000_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->cfg = cfg;
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ priv->i2c = i2c;
+
+ /* Check if firmware has been loaded. It is possible that another
+ instance of the driver has loaded the firmware.
+ */
+ if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0) {
+ kfree(priv);
+ return NULL;
+ }
+
+ switch(id) {
+ case XC_PRODUCT_ID_FW_LOADED:
+ printk(KERN_INFO
+ "xc5000: Successfully identified at address 0x%02x\n",
+ 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
+ "xc5000: Successfully identified at address 0x%02x\n",
+ cfg->i2c_address);
+ printk(KERN_INFO
+ "xc5000: Firmware has not been loaded previously\n");
+ priv->fwloaded = 0;
+ break;
+ default:
+ printk(KERN_ERR
+ "xc5000: Device not found at addr 0x%02x (0x%x)\n",
+ cfg->i2c_address, id);
+ kfree(priv);
+ return NULL;
+ }
+
+ memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+
+ return fe;
+}
+EXPORT_SYMBOL(xc5000_attach);
+
+MODULE_AUTHOR("Steven Toth");
+MODULE_DESCRIPTION("Xceive xc5000 silicon tuner driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/xc5000.h b/drivers/media/dvb/frontends/xc5000.h
new file mode 100644
index 000000000000..e0e84562aed1
--- /dev/null
+++ b/drivers/media/dvb/frontends/xc5000.h
@@ -0,0 +1,62 @@
+/*
+ * Driver for Xceive XC5000 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2007 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 __XC5000_H__
+#define __XC5000_H__
+
+#include <linux/firmware.h>
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+struct xc5000_config {
+ u8 i2c_address;
+ u32 if_khz;
+
+ /* For each bridge framework, when it attaches either analog or digital,
+ * it has to store a reference back to its _core equivalent structure,
+ * so that it can service the hardware by steering gpio's etc.
+ * Each bridge implementation is different so cast priv accordingly.
+ * The xc5000 driver cares not for this value, other than ensuring
+ * it's passed back to a bridge during tuner_callback().
+ */
+ void *priv;
+ int (*tuner_callback) (void *priv, int command, int arg);
+};
+
+/* xc5000 callback command */
+#define XC5000_TUNER_RESET 0
+
+#if defined(CONFIG_DVB_TUNER_XC5000) || defined(CONFIG_DVB_TUNER_XC5000_MODULE)
+extern struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct xc5000_config *cfg);
+#else
+static inline struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct xc5000_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TUNER_XC5000
+
+#endif // __XC5000_H__
diff --git a/drivers/media/dvb/frontends/xc5000_priv.h b/drivers/media/dvb/frontends/xc5000_priv.h
new file mode 100644
index 000000000000..13b2d19341da
--- /dev/null
+++ b/drivers/media/dvb/frontends/xc5000_priv.h
@@ -0,0 +1,36 @@
+/*
+ * Driver for Xceive XC5000 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2007 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 XC5000_PRIV_H
+#define XC5000_PRIV_H
+
+struct xc5000_priv {
+ struct xc5000_config *cfg;
+ struct i2c_adapter *i2c;
+
+ u32 freq_hz;
+ u32 bandwidth;
+ u8 video_standard;
+ u8 rf_mode;
+ u8 fwloaded;
+};
+
+#endif
diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c
index 0106df4c55e8..276e3b631dc2 100644
--- a/drivers/media/dvb/frontends/zl10353.c
+++ b/drivers/media/dvb/frontends/zl10353.c
@@ -1,7 +1,7 @@
/*
* Driver for Zarlink DVB-T ZL10353 demodulator
*
- * Copyright (C) 2006 Christopher Pascoe <c.pascoe@itee.uq.edu.au>
+ * Copyright (C) 2006, 2007 Christopher Pascoe <c.pascoe@itee.uq.edu.au>
*
* 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
@@ -16,7 +16,7 @@
*
* 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.=
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
@@ -25,6 +25,7 @@
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/slab.h>
+#include <asm/div64.h>
#include "dvb_frontend.h"
#include "zl10353_priv.h"
@@ -35,6 +36,8 @@ struct zl10353_state {
struct dvb_frontend frontend;
struct zl10353_config config;
+
+ enum fe_bandwidth bandwidth;
};
static int debug;
@@ -122,9 +125,10 @@ static void zl10353_calc_nominal_rate(struct dvb_frontend *fe,
enum fe_bandwidth bandwidth,
u16 *nominal_rate)
{
- u32 adc_clock = 45056; /* 45.056 MHz */
- u8 bw;
struct zl10353_state *state = fe->demodulator_priv;
+ u32 adc_clock = 450560; /* 45.056 MHz */
+ u64 value;
+ u8 bw;
if (state->config.adc_clock)
adc_clock = state->config.adc_clock;
@@ -142,12 +146,44 @@ static void zl10353_calc_nominal_rate(struct dvb_frontend *fe,
break;
}
- *nominal_rate = (bw * (1 << 23) / 7 * 125 + adc_clock / 2) / adc_clock;
+ value = (u64)10 * (1 << 23) / 7 * 125;
+ value = (bw * value) + adc_clock / 2;
+ do_div(value, adc_clock);
+ *nominal_rate = value;
dprintk("%s: bw %d, adc_clock %d => 0x%x\n",
__FUNCTION__, bw, adc_clock, *nominal_rate);
}
+static void zl10353_calc_input_freq(struct dvb_frontend *fe,
+ u16 *input_freq)
+{
+ struct zl10353_state *state = fe->demodulator_priv;
+ u32 adc_clock = 450560; /* 45.056 MHz */
+ int if2 = 361667; /* 36.1667 MHz */
+ int ife;
+ u64 value;
+
+ if (state->config.adc_clock)
+ adc_clock = state->config.adc_clock;
+ if (state->config.if2)
+ if2 = state->config.if2;
+
+ if (adc_clock >= if2 * 2)
+ ife = if2;
+ else {
+ ife = adc_clock - (if2 % adc_clock);
+ if (ife > adc_clock / 2)
+ ife = adc_clock - ife;
+ }
+ value = (u64)65536 * ife + adc_clock / 2;
+ do_div(value, adc_clock);
+ *input_freq = -value;
+
+ dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n",
+ __FUNCTION__, if2, ife, adc_clock, -(int)value, *input_freq);
+}
+
static int zl10353_sleep(struct dvb_frontend *fe)
{
static u8 zl10353_softdown[] = { 0x50, 0x0C, 0x44 };
@@ -160,64 +196,276 @@ static int zl10353_set_parameters(struct dvb_frontend *fe,
struct dvb_frontend_parameters *param)
{
struct zl10353_state *state = fe->demodulator_priv;
- u16 nominal_rate;
- u8 pllbuf[6] = { 0x67 };
+ u16 nominal_rate, input_freq;
+ u8 pllbuf[6] = { 0x67 }, acq_ctl = 0;
+ u16 tps = 0;
+ struct dvb_ofdm_parameters *op = &param->u.ofdm;
- /* These settings set "auto-everything" and start the FSM. */
- zl10353_single_write(fe, 0x55, 0x80);
+ zl10353_single_write(fe, RESET, 0x80);
udelay(200);
zl10353_single_write(fe, 0xEA, 0x01);
udelay(200);
zl10353_single_write(fe, 0xEA, 0x00);
- zl10353_single_write(fe, 0x56, 0x28);
- zl10353_single_write(fe, 0x89, 0x20);
- zl10353_single_write(fe, 0x5E, 0x00);
+ zl10353_single_write(fe, AGC_TARGET, 0x28);
+
+ if (op->transmission_mode != TRANSMISSION_MODE_AUTO)
+ acq_ctl |= (1 << 0);
+ if (op->guard_interval != GUARD_INTERVAL_AUTO)
+ acq_ctl |= (1 << 1);
+ zl10353_single_write(fe, ACQ_CTL, acq_ctl);
- zl10353_calc_nominal_rate(fe, param->u.ofdm.bandwidth, &nominal_rate);
+ switch (op->bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ /* These are extrapolated from the 7 and 8MHz values */
+ zl10353_single_write(fe, MCLK_RATIO, 0x97);
+ zl10353_single_write(fe, 0x64, 0x34);
+ break;
+ case BANDWIDTH_7_MHZ:
+ zl10353_single_write(fe, MCLK_RATIO, 0x86);
+ zl10353_single_write(fe, 0x64, 0x35);
+ break;
+ case BANDWIDTH_8_MHZ:
+ default:
+ zl10353_single_write(fe, MCLK_RATIO, 0x75);
+ zl10353_single_write(fe, 0x64, 0x36);
+ }
+
+ zl10353_calc_nominal_rate(fe, op->bandwidth, &nominal_rate);
zl10353_single_write(fe, TRL_NOMINAL_RATE_1, msb(nominal_rate));
zl10353_single_write(fe, TRL_NOMINAL_RATE_0, lsb(nominal_rate));
+ state->bandwidth = op->bandwidth;
+
+ zl10353_calc_input_freq(fe, &input_freq);
+ zl10353_single_write(fe, INPUT_FREQ_1, msb(input_freq));
+ zl10353_single_write(fe, INPUT_FREQ_0, lsb(input_freq));
+
+ /* Hint at TPS settings */
+ switch (op->code_rate_HP) {
+ case FEC_2_3:
+ tps |= (1 << 7);
+ break;
+ case FEC_3_4:
+ tps |= (2 << 7);
+ break;
+ case FEC_5_6:
+ tps |= (3 << 7);
+ break;
+ case FEC_7_8:
+ tps |= (4 << 7);
+ break;
+ case FEC_1_2:
+ case FEC_AUTO:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->code_rate_LP) {
+ case FEC_2_3:
+ tps |= (1 << 4);
+ break;
+ case FEC_3_4:
+ tps |= (2 << 4);
+ break;
+ case FEC_5_6:
+ tps |= (3 << 4);
+ break;
+ case FEC_7_8:
+ tps |= (4 << 4);
+ break;
+ case FEC_1_2:
+ case FEC_AUTO:
+ break;
+ case FEC_NONE:
+ if (op->hierarchy_information == HIERARCHY_AUTO ||
+ op->hierarchy_information == HIERARCHY_NONE)
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->constellation) {
+ case QPSK:
+ break;
+ case QAM_AUTO:
+ case QAM_16:
+ tps |= (1 << 13);
+ break;
+ case QAM_64:
+ tps |= (2 << 13);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ case TRANSMISSION_MODE_AUTO:
+ break;
+ case TRANSMISSION_MODE_8K:
+ tps |= (1 << 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->guard_interval) {
+ case GUARD_INTERVAL_1_32:
+ case GUARD_INTERVAL_AUTO:
+ break;
+ case GUARD_INTERVAL_1_16:
+ tps |= (1 << 2);
+ break;
+ case GUARD_INTERVAL_1_8:
+ tps |= (2 << 2);
+ break;
+ case GUARD_INTERVAL_1_4:
+ tps |= (3 << 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->hierarchy_information) {
+ case HIERARCHY_AUTO:
+ case HIERARCHY_NONE:
+ break;
+ case HIERARCHY_1:
+ tps |= (1 << 10);
+ break;
+ case HIERARCHY_2:
+ tps |= (2 << 10);
+ break;
+ case HIERARCHY_4:
+ tps |= (3 << 10);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ zl10353_single_write(fe, TPS_GIVEN_1, msb(tps));
+ zl10353_single_write(fe, TPS_GIVEN_0, lsb(tps));
- zl10353_single_write(fe, 0x6C, 0xCD);
- zl10353_single_write(fe, 0x6D, 0x7E);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
- // if there is no attached secondary tuner, we call set_params to program
- // a potential tuner attached somewhere else
+ /*
+ * If there is no tuner attached to the secondary I2C bus, we call
+ * set_params to program a potential tuner attached somewhere else.
+ * Otherwise, we update the PLL registers via calc_regs.
+ */
if (state->config.no_tuner) {
if (fe->ops.tuner_ops.set_params) {
fe->ops.tuner_ops.set_params(fe, param);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
}
+ } else if (fe->ops.tuner_ops.calc_regs) {
+ fe->ops.tuner_ops.calc_regs(fe, param, pllbuf + 1, 5);
+ pllbuf[1] <<= 1;
+ zl10353_write(fe, pllbuf, sizeof(pllbuf));
}
- // if pllbuf is defined, retrieve the settings
- if (fe->ops.tuner_ops.calc_regs) {
- fe->ops.tuner_ops.calc_regs(fe, param, pllbuf+1, 5);
- pllbuf[1] <<= 1;
- } else {
- // fake pllbuf settings
- pllbuf[1] = 0x61 << 1;
- pllbuf[2] = 0;
- pllbuf[3] = 0;
- pllbuf[3] = 0;
- pllbuf[4] = 0;
+ zl10353_single_write(fe, 0x5F, 0x13);
+
+ /* If no attached tuner or invalid PLL registers, just start the FSM. */
+ if (state->config.no_tuner || fe->ops.tuner_ops.calc_regs == NULL)
+ zl10353_single_write(fe, FSM_GO, 0x01);
+ else
+ zl10353_single_write(fe, TUNER_GO, 0x01);
+
+ return 0;
+}
+
+static int zl10353_get_parameters(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct zl10353_state *state = fe->demodulator_priv;
+ struct dvb_ofdm_parameters *op = &param->u.ofdm;
+ int s6, s9;
+ u16 tps;
+ static const u8 tps_fec_to_api[8] = {
+ FEC_1_2,
+ FEC_2_3,
+ FEC_3_4,
+ FEC_5_6,
+ FEC_7_8,
+ FEC_AUTO,
+ FEC_AUTO,
+ FEC_AUTO
+ };
+
+ s6 = zl10353_read_register(state, STATUS_6);
+ s9 = zl10353_read_register(state, STATUS_9);
+ if (s6 < 0 || s9 < 0)
+ return -EREMOTEIO;
+ if ((s6 & (1 << 5)) == 0 || (s9 & (1 << 4)) == 0)
+ return -EINVAL; /* no FE or TPS lock */
+
+ tps = zl10353_read_register(state, TPS_RECEIVED_1) << 8 |
+ zl10353_read_register(state, TPS_RECEIVED_0);
+
+ op->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7];
+ op->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7];
+
+ switch ((tps >> 13) & 3) {
+ case 0:
+ op->constellation = QPSK;
+ break;
+ case 1:
+ op->constellation = QAM_16;
+ break;
+ case 2:
+ op->constellation = QAM_64;
+ break;
+ default:
+ op->constellation = QAM_AUTO;
+ break;
}
- // there is no call to _just_ start decoding, so we send the pllbuf anyway
- // even if there isn't a PLL attached to the secondary bus
- zl10353_write(fe, pllbuf, sizeof(pllbuf));
+ op->transmission_mode = (tps & 0x01) ? TRANSMISSION_MODE_8K :
+ TRANSMISSION_MODE_2K;
- zl10353_single_write(fe, 0x5F, 0x13);
- zl10353_single_write(fe, 0x70, 0x01);
- udelay(250);
- zl10353_single_write(fe, 0xE4, 0x00);
- zl10353_single_write(fe, 0xE5, 0x2A);
- zl10353_single_write(fe, 0xE9, 0x02);
- zl10353_single_write(fe, 0xE7, 0x40);
- zl10353_single_write(fe, 0xE8, 0x10);
+ switch ((tps >> 2) & 3) {
+ case 0:
+ op->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ op->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ op->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ op->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ default:
+ op->guard_interval = GUARD_INTERVAL_AUTO;
+ break;
+ }
+
+ switch ((tps >> 10) & 7) {
+ case 0:
+ op->hierarchy_information = HIERARCHY_NONE;
+ break;
+ case 1:
+ op->hierarchy_information = HIERARCHY_1;
+ break;
+ case 2:
+ op->hierarchy_information = HIERARCHY_2;
+ break;
+ case 3:
+ op->hierarchy_information = HIERARCHY_4;
+ break;
+ default:
+ op->hierarchy_information = HIERARCHY_AUTO;
+ break;
+ }
+
+ param->frequency = 0;
+ op->bandwidth = state->bandwidth;
+ param->inversion = INVERSION_AUTO;
return 0;
}
@@ -406,6 +654,7 @@ static struct dvb_frontend_ops zl10353_ops = {
.write = zl10353_write,
.set_frontend = zl10353_set_parameters,
+ .get_frontend = zl10353_get_parameters,
.get_tune_settings = zl10353_get_tune_settings,
.read_status = zl10353_read_status,
diff --git a/drivers/media/dvb/frontends/zl10353.h b/drivers/media/dvb/frontends/zl10353.h
index 1c3d494a6da9..fc734c22b5fa 100644
--- a/drivers/media/dvb/frontends/zl10353.h
+++ b/drivers/media/dvb/frontends/zl10353.h
@@ -1,7 +1,7 @@
/*
* Driver for Zarlink DVB-T ZL10353 demodulator
*
- * Copyright (C) 2006 Christopher Pascoe <c.pascoe@itee.uq.edu.au>
+ * Copyright (C) 2006, 2007 Christopher Pascoe <c.pascoe@itee.uq.edu.au>
*
* 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
@@ -29,8 +29,9 @@ struct zl10353_config
/* demodulator's I2C address */
u8 demod_address;
- /* frequencies in kHz */
- int adc_clock; /* default: 45056 */
+ /* frequencies in units of 0.1kHz */
+ int adc_clock; /* default: 450560 (45.056 MHz) */
+ int if2; /* default: 361667 (36.1667 MHz) */
/* set if no pll is connected to the secondary i2c bus */
int no_tuner;
@@ -49,6 +50,6 @@ static inline struct dvb_frontend* zl10353_attach(const struct zl10353_config *c
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
return NULL;
}
-#endif // CONFIG_DVB_ZL10353
+#endif /* CONFIG_DVB_ZL10353 */
#endif /* ZL10353_H */
diff --git a/drivers/media/dvb/frontends/zl10353_priv.h b/drivers/media/dvb/frontends/zl10353_priv.h
index 4962434b35e7..055ff1f7e349 100644
--- a/drivers/media/dvb/frontends/zl10353_priv.h
+++ b/drivers/media/dvb/frontends/zl10353_priv.h
@@ -1,7 +1,7 @@
/*
* Driver for Zarlink DVB-T ZL10353 demodulator
*
- * Copyright (C) 2006 Christopher Pascoe <c.pascoe@itee.uq.edu.au>
+ * Copyright (C) 2006, 2007 Christopher Pascoe <c.pascoe@itee.uq.edu.au>
*
* 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
@@ -16,7 +16,7 @@
*
* 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.=
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ZL10353_PRIV_
@@ -46,9 +46,28 @@ enum zl10353_reg_addr {
RS_ERR_CNT_0 = 0x13,
RS_UBC_1 = 0x14,
RS_UBC_0 = 0x15,
+ TPS_RECEIVED_1 = 0x1D,
+ TPS_RECEIVED_0 = 0x1E,
+ TPS_CURRENT_1 = 0x1F,
+ TPS_CURRENT_0 = 0x20,
+ RESET = 0x55,
+ AGC_TARGET = 0x56,
+ MCLK_RATIO = 0x5C,
+ ACQ_CTL = 0x5E,
TRL_NOMINAL_RATE_1 = 0x65,
TRL_NOMINAL_RATE_0 = 0x66,
+ INPUT_FREQ_1 = 0x6C,
+ INPUT_FREQ_0 = 0x6D,
+ TPS_GIVEN_1 = 0x6E,
+ TPS_GIVEN_0 = 0x6F,
+ TUNER_GO = 0x70,
+ FSM_GO = 0x71,
CHIP_ID = 0x7F,
+ CHAN_STEP_1 = 0xE4,
+ CHAN_STEP_0 = 0xE5,
+ OFDM_LOCK_TIME = 0xE7,
+ FEC_LOCK_TIME = 0xE8,
+ ACQ_DELAY = 0xE9,
};
#endif /* _ZL10353_PRIV_ */
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
index 54b91f26ca63..ae882432dd3d 100644
--- a/drivers/media/dvb/ttpci/Kconfig
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -1,8 +1,14 @@
+config TTPCI_EEPROM
+ tristate
+ default n
+
config DVB_AV7110
tristate "AV7110 cards"
- depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
+ depends on DVB_CORE && PCI && I2C
select FW_LOADER if !DVB_AV7110_FIRMWARE
+ select TTPCI_EEPROM
select VIDEO_SAA7146_VV
+ depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV
select DVB_VES1820 if !DVB_FE_CUSTOMISE
select DVB_VES1X93 if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
@@ -57,10 +63,19 @@ config DVB_AV7110_OSD
All other people say N.
+config DVB_BUDGET_CORE
+ tristate "SAA7146 DVB cards (aka Budget, Nova-PCI)"
+ depends on DVB_CORE && PCI && I2C
+ select VIDEO_SAA7146
+ select TTPCI_EEPROM
+ help
+ Support for simple SAA7146 based DVB cards
+ (so called Budget- or Nova-PCI cards) without onboard
+ MPEG2 decoder.
+
config DVB_BUDGET
tristate "Budget cards"
- depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
- select VIDEO_SAA7146
+ depends on DVB_BUDGET_CORE && I2C
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_VES1X93 if !DVB_FE_CUSTOMISE
select DVB_VES1820 if !DVB_FE_CUSTOMISE
@@ -73,9 +88,9 @@ config DVB_BUDGET
select DVB_TDA826X if !DVB_FE_CUSTOMISE
select DVB_LNBP21 if !DVB_FE_CUSTOMISE
help
- Support for simple SAA7146 based DVB cards
- (so called Budget- or Nova-PCI cards) without onboard
- MPEG2 decoder.
+ Support for simple SAA7146 based DVB cards (so called Budget-
+ or Nova-PCI cards) without onboard MPEG2 decoder, and without
+ analog inputs or an onboard Common Interface connector.
Say Y if you own such a card and want to use it.
@@ -84,8 +99,7 @@ config DVB_BUDGET
config DVB_BUDGET_CI
tristate "Budget cards with onboard CI connector"
- depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 && INPUT
- select VIDEO_SAA7146
+ depends on DVB_BUDGET_CORE && I2C
select DVB_STV0297 if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
@@ -106,8 +120,9 @@ config DVB_BUDGET_CI
config DVB_BUDGET_AV
tristate "Budget cards with analog video inputs"
- depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
+ depends on DVB_BUDGET_CORE && I2C
select VIDEO_SAA7146_VV
+ depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV
select DVB_PLL if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
@@ -127,8 +142,8 @@ config DVB_BUDGET_AV
config DVB_BUDGET_PATCH
tristate "AV7110 cards with Budget Patch"
- depends on DVB_CORE && DVB_BUDGET && VIDEO_V4L1
- select DVB_AV7110
+ depends on DVB_BUDGET_CORE && I2C
+ depends on DVB_AV7110
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_VES1X93 if !DVB_FE_CUSTOMISE
select DVB_TDA8083 if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/dvb/ttpci/Makefile b/drivers/media/dvb/ttpci/Makefile
index 2c1145236ee6..d7483f1a9b3f 100644
--- a/drivers/media/dvb/ttpci/Makefile
+++ b/drivers/media/dvb/ttpci/Makefile
@@ -5,11 +5,13 @@
dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o av7110_ir.o
-obj-$(CONFIG_DVB_BUDGET) += budget-core.o budget.o ttpci-eeprom.o
-obj-$(CONFIG_DVB_BUDGET_AV) += budget-core.o budget-av.o ttpci-eeprom.o
-obj-$(CONFIG_DVB_BUDGET_CI) += budget-core.o budget-ci.o ttpci-eeprom.o
-obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-core.o budget-patch.o ttpci-eeprom.o
-obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o ttpci-eeprom.o
+obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o
+obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o
+obj-$(CONFIG_DVB_BUDGET) += budget.o
+obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o
+obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o
+obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o
+obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index 0d36c155695b..0e5701bdff19 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -2595,7 +2595,8 @@ static int __devinit av7110_attach(struct saa7146_dev* dev,
mutex_init(&av7110->osd_mutex);
/* TV standard */
- av7110->vidmode = tv_standard == 1 ? VIDEO_MODE_NTSC : VIDEO_MODE_PAL;
+ av7110->vidmode = tv_standard == 1 ? AV7110_VIDEO_MODE_NTSC
+ : AV7110_VIDEO_MODE_PAL;
/* ARM "watchdog" */
init_waitqueue_head(&av7110->arm_wait);
diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h
index 0cb439527498..39fbf7d5cffb 100644
--- a/drivers/media/dvb/ttpci/av7110.h
+++ b/drivers/media/dvb/ttpci/av7110.h
@@ -46,6 +46,11 @@ extern int av7110_debug;
enum {AV_PES_STREAM, PS_STREAM, TS_STREAM, PES_STREAM};
+enum av7110_video_mode {
+ AV7110_VIDEO_MODE_PAL = 0,
+ AV7110_VIDEO_MODE_NTSC = 1
+};
+
struct av7110_p2t {
u8 pes[TS_SIZE];
u8 counter;
@@ -170,7 +175,7 @@ struct av7110 {
ca_slot_info_t ci_slot[2];
- int vidmode;
+ enum av7110_video_mode vidmode;
struct dmxdev dmxdev;
struct dvb_demux demux;
diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c
index d75e7e48addc..aef6e36d7c5c 100644
--- a/drivers/media/dvb/ttpci/av7110_av.c
+++ b/drivers/media/dvb/ttpci/av7110_av.c
@@ -329,7 +329,7 @@ int av7110_set_volume(struct av7110 *av7110, int volleft, int volright)
return 0;
}
-int av7110_set_vidmode(struct av7110 *av7110, int mode)
+int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode)
{
int ret;
dprintk(2, "av7110:%p, \n", av7110);
@@ -348,11 +348,15 @@ int av7110_set_vidmode(struct av7110 *av7110, int mode)
}
-static int sw2mode[16] = {
- VIDEO_MODE_PAL, VIDEO_MODE_NTSC, VIDEO_MODE_NTSC, VIDEO_MODE_PAL,
- VIDEO_MODE_NTSC, VIDEO_MODE_NTSC, VIDEO_MODE_PAL, VIDEO_MODE_NTSC,
- VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL,
- VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL,
+static enum av7110_video_mode sw2mode[16] = {
+ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC,
+ AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_PAL,
+ AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_NTSC,
+ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC,
+ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL,
+ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL,
+ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL,
+ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL,
};
static int get_video_format(struct av7110 *av7110, u8 *buf, int count)
diff --git a/drivers/media/dvb/ttpci/av7110_av.h b/drivers/media/dvb/ttpci/av7110_av.h
index 45dc144b8b43..5f02ef85e47d 100644
--- a/drivers/media/dvb/ttpci/av7110_av.h
+++ b/drivers/media/dvb/ttpci/av7110_av.h
@@ -3,7 +3,8 @@
struct av7110;
-extern int av7110_set_vidmode(struct av7110 *av7110, int mode);
+extern int av7110_set_vidmode(struct av7110 *av7110,
+ enum av7110_video_mode mode);
extern int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len);
extern int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen);
diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
index 76cca003252f..e2f066fb7967 100644
--- a/drivers/media/dvb/ttpci/av7110_v4l.c
+++ b/drivers/media/dvb/ttpci/av7110_v4l.c
@@ -876,11 +876,11 @@ static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
if (std->id & V4L2_STD_PAL) {
- av7110->vidmode = VIDEO_MODE_PAL;
+ av7110->vidmode = AV7110_VIDEO_MODE_PAL;
av7110_set_vidmode(av7110, av7110->vidmode);
}
else if (std->id & V4L2_STD_NTSC) {
- av7110->vidmode = VIDEO_MODE_NTSC;
+ av7110->vidmode = AV7110_VIDEO_MODE_NTSC;
av7110_set_vidmode(av7110, av7110->vidmode);
}
else
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 11e962f1a97f..8d5214f18cf0 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -351,4 +351,14 @@ config USB_DSBR
To compile this driver as a module, choose M here: the
module will be called dsbr100.
+config USB_SI470X
+ tristate "Silicon Labs Si470x FM Radio Receiver support"
+ depends on USB && VIDEO_V4L2
+ ---help---
+ Say Y here if you want to connect this type of radio to your
+ computer's USB port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-silabs.
+
endif # RADIO_ADAPTERS
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index cf55a18e3ddf..a30159f6fa42 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -21,5 +21,6 @@ obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o
obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
+obj-$(CONFIG_USB_SI470X) += radio-si470x.o
EXTRA_CFLAGS += -Isound
diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c
index 3bd07f7e3774..36c0e3651502 100644
--- a/drivers/media/radio/dsbr100.c
+++ b/drivers/media/radio/dsbr100.c
@@ -33,6 +33,9 @@
History:
+ Version 0.43:
+ Oliver Neukum: avoided DMA coherency issue
+
Version 0.42:
Converted dsbr100 to use video_ioctl2
by Douglas Landgraf <dougsland@gmail.com>
@@ -135,7 +138,7 @@ module_param(radio_nr, int, 0);
struct dsbr100_device {
struct usb_device *usbdev;
struct video_device *videodev;
- unsigned char transfer_buffer[TB_LEN];
+ u8 *transfer_buffer;
int curfreq;
int stereo;
int users;
@@ -237,10 +240,7 @@ static void dsbr100_getstat(struct dsbr100_device *radio)
/* handle unplugging of the device, release data structures
if nothing keeps us from doing it. If something is still
keeping us busy, the release callback of v4l will take care
-of releasing it. stv680.c does not relase its private
-data, so I don't do this here either. Checking out the
-code I'd expect I better did that, but if there's a memory
-leak here it's tiny (~50 bytes per disconnect) */
+of releasing it. */
static void usb_dsbr100_disconnect(struct usb_interface *intf)
{
struct dsbr100_device *radio = usb_get_intfdata(intf);
@@ -250,6 +250,7 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf)
video_unregister_device(radio->videodev);
radio->videodev = NULL;
if (radio->users) {
+ kfree(radio->transfer_buffer);
kfree(radio);
} else {
radio->removed = 1;
@@ -425,6 +426,7 @@ static int usb_dsbr100_close(struct inode *inode, struct file *file)
return -ENODEV;
radio->users = 0;
if (radio->removed) {
+ kfree(radio->transfer_buffer);
kfree(radio);
}
return 0;
@@ -471,7 +473,12 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
if (!(radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL)))
return -ENOMEM;
+ if (!(radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL))) {
+ kfree(radio);
+ return -ENOMEM;
+ }
if (!(radio->videodev = video_device_alloc())) {
+ kfree(radio->transfer_buffer);
kfree(radio);
return -ENOMEM;
}
@@ -485,6 +492,7 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
if (video_register_device(radio->videodev, VFL_TYPE_RADIO,radio_nr)) {
warn("Could not register video device");
video_device_release(radio->videodev);
+ kfree(radio->transfer_buffer);
kfree(radio);
return -EIO;
}
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index 5e4b9ddb23c0..246422b49267 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -58,10 +58,10 @@ static int initmute = 1;
static int radio_nr = -1;
module_param(io, int, 0444);
-MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic"
+MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic "
"probing is disabled or fails. The most common I/O ports are: 0x20c "
"0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to "
- " work for the combined sound/radiocard).");
+ "work for the combined sound/radiocard).");
module_param(probe, bool, 0444);
MODULE_PARM_DESC(probe, "Enable automatic device probing. Note: only the most "
@@ -392,7 +392,7 @@ static struct v4l2_queryctrl radio_qctrl[] = {
}
};
-static struct file_operations gemtek_fops = {
+static const struct file_operations gemtek_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c
index 8e33a19a22a3..bc51f4d23a5a 100644
--- a/drivers/media/radio/radio-maestro.c
+++ b/drivers/media/radio/radio-maestro.c
@@ -423,7 +423,7 @@ static int __devinit maestro_probe(struct pci_dev *pdev,
errunr:
video_unregister_device(maestro_radio_inst);
errfr1:
- kfree(maestro_radio_inst);
+ video_device_release(maestro_radio_inst);
errfr:
kfree(radio_unit);
err:
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index 395165367f37..3118bdab3183 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -321,7 +321,7 @@ static struct isapnp_device_id id_table[] __devinitdata = {
MODULE_DEVICE_TABLE(isapnp, id_table);
-static int isapnp_fmi_probe(void)
+static int __init isapnp_fmi_probe(void)
{
int i = 0;
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index c432c44bd634..f7c8b000404f 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -476,8 +476,7 @@ static int __init fmr2_init(void)
return -EBUSY;
}
- if(video_register_device(&fmr2_radio, VFL_TYPE_RADIO, radio_nr)==-1)
- {
+ if (video_register_device(&fmr2_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
release_region(io, 2);
return -EINVAL;
}
diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c
new file mode 100644
index 000000000000..8e4bd4769048
--- /dev/null
+++ b/drivers/media/radio/radio-si470x.c
@@ -0,0 +1,1432 @@
+/*
+ * drivers/media/radio/radio-si470x.c
+ *
+ * Driver for USB radios for the Silicon Labs Si470x FM Radio Receivers:
+ * - Silicon Labs USB FM Radio Reference Design
+ * - ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF)
+ *
+ * Copyright (c) 2008 Tobias Lorenz <tobias.lorenz@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/*
+ * History:
+ * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.0
+ * - First working version
+ * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.1
+ * - Improved error handling, every function now returns errno
+ * - Improved multi user access (start/mute/stop)
+ * - Channel doesn't get lost anymore after start/mute/stop
+ * - RDS support added (polling mode via interrupt EP 1)
+ * - marked default module parameters with *value*
+ * - switched from bit structs to bit masks
+ * - header file cleaned and integrated
+ * 2008-01-14 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.2
+ * - hex values are now lower case
+ * - commented USB ID for ADS/Tech moved on todo list
+ * - blacklisted si470x in hid-quirks.c
+ * - rds buffer handling functions integrated into *_work, *_read
+ * - rds_command in si470x_poll exchanged against simple retval
+ * - check for firmware version 15
+ * - code order and prototypes still remain the same
+ * - spacing and bottom of band codes remain the same
+ * 2008-01-16 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.3
+ * - code reordered to avoid function prototypes
+ * - switch/case defaults are now more user-friendly
+ * - unified comment style
+ * - applied all checkpatch.pl v1.12 suggestions
+ * except the warning about the too long lines with bit comments
+ * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
+ * 2008-01-22 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.4
+ * - avoid poss. locking when doing copy_to_user which may sleep
+ * - RDS is automatically activated on read now
+ * - code cleaned of unnecessary rds_commands
+ * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
+ * (thanks to Guillaume RAMOUSSE)
+ *
+ * ToDo:
+ * - add seeking support
+ * - add firmware download/update support
+ * - RDS support: interrupt mode, instead of polling
+ * - add LED status output (check if that's not already done in firmware)
+ */
+
+
+/* driver definitions */
+#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
+#define DRIVER_NAME "radio-si470x"
+#define DRIVER_VERSION KERNEL_VERSION(1, 0, 4)
+#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
+#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
+
+
+/* kernel includes */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/hid.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/rds.h>
+
+
+/* USB Device ID List */
+static struct usb_device_id si470x_usb_driver_id_table[] = {
+ /* Silicon Labs USB FM Radio Reference Design */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
+ /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },
+ /* Terminating entry */
+ { }
+};
+MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table);
+
+
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Radio Nr */
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+
+/* Spacing (kHz) */
+/* 0: 200 kHz (USA, Australia) */
+/* 1: 100 kHz (Europe, Japan) */
+/* 2: 50 kHz */
+static int space = 2;
+module_param(space, int, 0);
+MODULE_PARM_DESC(radio_nr, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
+
+/* Bottom of Band (MHz) */
+/* 0: 87.5 - 108 MHz (USA, Europe)*/
+/* 1: 76 - 108 MHz (Japan wide band) */
+/* 2: 76 - 90 MHz (Japan) */
+static int band = 1;
+module_param(band, int, 0);
+MODULE_PARM_DESC(radio_nr, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
+
+/* De-emphasis */
+/* 0: 75 us (USA) */
+/* 1: 50 us (Europe, Australia, Japan) */
+static int de = 1;
+module_param(de, int, 0);
+MODULE_PARM_DESC(radio_nr, "De-emphasis: 0=75us *1=50us*");
+
+/* USB timeout */
+static int usb_timeout = 500;
+module_param(usb_timeout, int, 0);
+MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*");
+
+/* Seek retries */
+static int seek_retries = 100;
+module_param(seek_retries, int, 0);
+MODULE_PARM_DESC(seek_retries, "Seek retries: *100*");
+
+/* RDS buffer blocks */
+static int rds_buf = 100;
+module_param(rds_buf, int, 0);
+MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
+
+/* RDS maximum block errors */
+static int max_rds_errors = 1;
+/* 0 means 0 errors requiring correction */
+/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */
+/* 2 means 3-5 errors requiring correction */
+/* 3 means 6+ errors or errors in checkword, correction not possible */
+module_param(max_rds_errors, int, 0);
+MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
+
+/* RDS poll frequency */
+static int rds_poll_time = 40;
+/* 40 is used by the original USBRadio.exe */
+/* 50 is used by radio-cadet */
+/* 75 should be okay */
+/* 80 is the usual RDS receive interval */
+module_param(rds_poll_time, int, 0);
+MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*");
+
+
+
+/**************************************************************************
+ * Register Definitions
+ **************************************************************************/
+#define RADIO_REGISTER_SIZE 2 /* 16 register bit width */
+#define RADIO_REGISTER_NUM 16 /* DEVICEID ... RDSD */
+#define RDS_REGISTER_NUM 6 /* STATUSRSSI ... RDSD */
+
+#define DEVICEID 0 /* Device ID */
+#define DEVICEID_PN 0xf000 /* bits 15..12: Part Number */
+#define DEVICEID_MFGID 0x0fff /* bits 11..00: Manufacturer ID */
+
+#define CHIPID 1 /* Chip ID */
+#define CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */
+#define CHIPID_DEV 0x0200 /* bits 09..09: Device */
+#define CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */
+
+#define POWERCFG 2 /* Power Configuration */
+#define POWERCFG_DSMUTE 0x8000 /* bits 15..15: Softmute Disable */
+#define POWERCFG_DMUTE 0x4000 /* bits 14..14: Mute Disable */
+#define POWERCFG_MONO 0x2000 /* bits 13..13: Mono Select */
+#define POWERCFG_RDSM 0x0800 /* bits 11..11: RDS Mode (Si4701 only) */
+#define POWERCFG_SKMODE 0x0400 /* bits 10..10: Seek Mode */
+#define POWERCFG_SEEKUP 0x0200 /* bits 09..09: Seek Direction */
+#define POWERCFG_SEEK 0x0100 /* bits 08..08: Seek */
+#define POWERCFG_DISABLE 0x0040 /* bits 06..06: Powerup Disable */
+#define POWERCFG_ENABLE 0x0001 /* bits 00..00: Powerup Enable */
+
+#define CHANNEL 3 /* Channel */
+#define CHANNEL_TUNE 0x8000 /* bits 15..15: Tune */
+#define CHANNEL_CHAN 0x03ff /* bits 09..00: Channel Select */
+
+#define SYSCONFIG1 4 /* System Configuration 1 */
+#define SYSCONFIG1_RDSIEN 0x8000 /* bits 15..15: RDS Interrupt Enable (Si4701 only) */
+#define SYSCONFIG1_STCIEN 0x4000 /* bits 14..14: Seek/Tune Complete Interrupt Enable */
+#define SYSCONFIG1_RDS 0x1000 /* bits 12..12: RDS Enable (Si4701 only) */
+#define SYSCONFIG1_DE 0x0800 /* bits 11..11: De-emphasis (0=75us 1=50us) */
+#define SYSCONFIG1_AGCD 0x0400 /* bits 10..10: AGC Disable */
+#define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */
+#define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */
+#define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */
+#define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */
+
+#define SYSCONFIG2 5 /* System Configuration 2 */
+#define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */
+#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */
+#define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */
+#define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */
+
+#define SYSCONFIG3 6 /* System Configuration 3 */
+#define SYSCONFIG3_SMUTER 0xc000 /* bits 15..14: Softmute Attack/Recover Rate */
+#define SYSCONFIG3_SMUTEA 0x3000 /* bits 13..12: Softmute Attenuation */
+#define SYSCONFIG3_SKSNR 0x00f0 /* bits 07..04: Seek SNR Threshold */
+#define SYSCONFIG3_SKCNT 0x000f /* bits 03..00: Seek FM Impulse Detection Threshold */
+
+#define TEST1 7 /* Test 1 */
+#define TEST1_AHIZEN 0x4000 /* bits 14..14: Audio High-Z Enable */
+
+#define TEST2 8 /* Test 2 */
+/* TEST2 only contains reserved bits */
+
+#define BOOTCONFIG 9 /* Boot Configuration */
+/* BOOTCONFIG only contains reserved bits */
+
+#define STATUSRSSI 10 /* Status RSSI */
+#define STATUSRSSI_RDSR 0x8000 /* bits 15..15: RDS Ready (Si4701 only) */
+#define STATUSRSSI_STC 0x4000 /* bits 14..14: Seek/Tune Complete */
+#define STATUSRSSI_SF 0x2000 /* bits 13..13: Seek Fail/Band Limit */
+#define STATUSRSSI_AFCRL 0x1000 /* bits 12..12: AFC Rail */
+#define STATUSRSSI_RDSS 0x0800 /* bits 11..11: RDS Synchronized (Si4701 only) */
+#define STATUSRSSI_BLERA 0x0600 /* bits 10..09: RDS Block A Errors (Si4701 only) */
+#define STATUSRSSI_ST 0x0100 /* bits 08..08: Stereo Indicator */
+#define STATUSRSSI_RSSI 0x00ff /* bits 07..00: RSSI (Received Signal Strength Indicator) */
+
+#define READCHAN 11 /* Read Channel */
+#define READCHAN_BLERB 0xc000 /* bits 15..14: RDS Block D Errors (Si4701 only) */
+#define READCHAN_BLERC 0x3000 /* bits 13..12: RDS Block C Errors (Si4701 only) */
+#define READCHAN_BLERD 0x0c00 /* bits 11..10: RDS Block B Errors (Si4701 only) */
+#define READCHAN_READCHAN 0x03ff /* bits 09..00: Read Channel */
+
+#define RDSA 12 /* RDSA */
+#define RDSA_RDSA 0xffff /* bits 15..00: RDS Block A Data (Si4701 only) */
+
+#define RDSB 13 /* RDSB */
+#define RDSB_RDSB 0xffff /* bits 15..00: RDS Block B Data (Si4701 only) */
+
+#define RDSC 14 /* RDSC */
+#define RDSC_RDSC 0xffff /* bits 15..00: RDS Block C Data (Si4701 only) */
+
+#define RDSD 15 /* RDSD */
+#define RDSD_RDSD 0xffff /* bits 15..00: RDS Block D Data (Si4701 only) */
+
+
+
+/**************************************************************************
+ * USB HID Reports
+ **************************************************************************/
+
+/* Reports 1-16 give direct read/write access to the 16 Si470x registers */
+/* with the (REPORT_ID - 1) corresponding to the register address across USB */
+/* endpoint 0 using GET_REPORT and SET_REPORT */
+#define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1)
+#define REGISTER_REPORT(reg) ((reg) + 1)
+
+/* Report 17 gives direct read/write access to the entire Si470x register */
+/* map across endpoint 0 using GET_REPORT and SET_REPORT */
+#define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
+#define ENTIRE_REPORT 17
+
+/* Report 18 is used to send the lowest 6 Si470x registers up the HID */
+/* interrupt endpoint 1 to Windows every 20 milliseconds for status */
+#define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
+#define RDS_REPORT 18
+
+/* Report 19: LED state */
+#define LED_REPORT_SIZE 3
+#define LED_REPORT 19
+
+/* Report 19: stream */
+#define STREAM_REPORT_SIZE 3
+#define STREAM_REPORT 19
+
+/* Report 20: scratch */
+#define SCRATCH_PAGE_SIZE 63
+#define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1)
+#define SCRATCH_REPORT 20
+
+/* Reports 19-22: flash upgrade of the C8051F321 */
+#define WRITE_REPORT 19
+#define FLASH_REPORT 20
+#define CRC_REPORT 21
+#define RESPONSE_REPORT 22
+
+/* Report 23: currently unused, but can accept 60 byte reports on the HID */
+/* interrupt out endpoint 2 every 1 millisecond */
+#define UNUSED_REPORT 23
+
+
+
+/**************************************************************************
+ * Software/Hardware Versions
+ **************************************************************************/
+#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6
+#define RADIO_SW_VERSION 7
+#define RADIO_SW_VERSION_CURRENT 15
+#define RADIO_HW_VERSION 1
+
+#define SCRATCH_PAGE_SW_VERSION 1
+#define SCRATCH_PAGE_HW_VERSION 2
+
+
+
+/**************************************************************************
+ * LED State Definitions
+ **************************************************************************/
+#define LED_COMMAND 0x35
+
+#define NO_CHANGE_LED 0x00
+#define ALL_COLOR_LED 0x01 /* streaming state */
+#define BLINK_GREEN_LED 0x02 /* connect state */
+#define BLINK_RED_LED 0x04
+#define BLINK_ORANGE_LED 0x10 /* disconnect state */
+#define SOLID_GREEN_LED 0x20 /* tuning/seeking state */
+#define SOLID_RED_LED 0x40 /* bootload state */
+#define SOLID_ORANGE_LED 0x80
+
+
+
+/**************************************************************************
+ * Stream State Definitions
+ **************************************************************************/
+#define STREAM_COMMAND 0x36
+#define STREAM_VIDPID 0x00
+#define STREAM_AUDIO 0xff
+
+
+
+/**************************************************************************
+ * Bootloader / Flash Commands
+ **************************************************************************/
+
+/* unique id sent to bootloader and required to put into a bootload state */
+#define UNIQUE_BL_ID 0x34
+
+/* mask for the flash data */
+#define FLASH_DATA_MASK 0x55
+
+/* bootloader commands */
+#define GET_SW_VERSION_COMMAND 0x00
+#define SET_PAGE_COMMAND 0x01
+#define ERASE_PAGE_COMMAND 0x02
+#define WRITE_PAGE_COMMAND 0x03
+#define CRC_ON_PAGE_COMMAND 0x04
+#define READ_FLASH_BYTE_COMMAND 0x05
+#define RESET_DEVICE_COMMAND 0x06
+#define GET_HW_VERSION_COMMAND 0x07
+#define BLANK 0xff
+
+/* bootloader command responses */
+#define COMMAND_OK 0x01
+#define COMMAND_FAILED 0x02
+#define COMMAND_PENDING 0x03
+
+/* buffer sizes */
+#define COMMAND_BUFFER_SIZE 4
+#define RESPONSE_BUFFER_SIZE 2
+#define FLASH_BUFFER_SIZE 64
+#define CRC_BUFFER_SIZE 3
+
+
+
+/**************************************************************************
+ * General Driver Definitions
+ **************************************************************************/
+
+/*
+ * si470x_device - private data
+ */
+struct si470x_device {
+ /* reference to USB and video device */
+ struct usb_device *usbdev;
+ struct video_device *videodev;
+
+ /* are these really necessary ? */
+ int users;
+
+ /* report buffer (maximum 64 bytes) */
+ unsigned char buf[64];
+
+ /* Silabs internal registers (0..15) */
+ unsigned short registers[RADIO_REGISTER_NUM];
+
+ /* RDS receive buffer */
+ struct work_struct work;
+ wait_queue_head_t read_queue;
+ struct timer_list timer;
+ spinlock_t lock; /* buffer locking */
+ unsigned char *buffer; /* size is always multiple of three */
+ unsigned int buf_size;
+ unsigned int rd_index;
+ unsigned int wr_index;
+};
+
+
+/*
+ * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
+ * 62.5 kHz otherwise.
+ * The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
+ * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW
+ * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000
+ */
+#define FREQ_MUL (1000000 / 62.5)
+
+
+
+/**************************************************************************
+ * General Driver Functions
+ **************************************************************************/
+
+/*
+ * si470x_get_report - receive a HID report
+ */
+static int si470x_get_report(struct si470x_device *radio, int size)
+{
+ return usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ HID_REQ_GET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+ radio->buf[0], 2,
+ radio->buf, size, usb_timeout);
+}
+
+
+/*
+ * si470x_set_report - send a HID report
+ */
+static int si470x_set_report(struct si470x_device *radio, int size)
+{
+ return usb_control_msg(radio->usbdev,
+ usb_sndctrlpipe(radio->usbdev, 0),
+ HID_REQ_SET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ radio->buf[0], 2,
+ radio->buf, size, usb_timeout);
+}
+
+
+/*
+ * si470x_get_register - read register
+ */
+static int si470x_get_register(struct si470x_device *radio, int regnr)
+{
+ int retval;
+
+ radio->buf[0] = REGISTER_REPORT(regnr);
+
+ retval = si470x_get_report(radio, REGISTER_REPORT_SIZE);
+ if (retval >= 0)
+ radio->registers[regnr] = (radio->buf[1] << 8) | radio->buf[2];
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+/*
+ * si470x_set_register - write register
+ */
+static int si470x_set_register(struct si470x_device *radio, int regnr)
+{
+ int retval;
+
+ radio->buf[0] = REGISTER_REPORT(regnr);
+ radio->buf[1] = (radio->registers[regnr] & 0xff00) >> 8;
+ radio->buf[2] = (radio->registers[regnr] & 0x00ff);
+
+ retval = si470x_set_report(radio, REGISTER_REPORT_SIZE);
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+/*
+ * si470x_get_all_registers - read entire registers
+ */
+static int si470x_get_all_registers(struct si470x_device *radio)
+{
+ int retval;
+ int regnr;
+
+ radio->buf[0] = ENTIRE_REPORT;
+
+ retval = si470x_get_report(radio, ENTIRE_REPORT_SIZE);
+
+ if (retval >= 0)
+ for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
+ radio->registers[regnr] =
+ (radio->buf[regnr * RADIO_REGISTER_SIZE + 1] << 8) |
+ radio->buf[regnr * RADIO_REGISTER_SIZE + 2];
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+/*
+ * si470x_get_rds_registers - read rds registers
+ */
+static int si470x_get_rds_registers(struct si470x_device *radio)
+{
+ int retval;
+ int regnr;
+ int size;
+
+ radio->buf[0] = RDS_REPORT;
+
+ retval = usb_interrupt_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 1),
+ radio->buf, RDS_REPORT_SIZE, &size, usb_timeout);
+
+ if (retval >= 0)
+ for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
+ radio->registers[STATUSRSSI + regnr] =
+ (radio->buf[regnr * RADIO_REGISTER_SIZE + 1] << 8) |
+ radio->buf[regnr * RADIO_REGISTER_SIZE + 2];
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+/*
+ * si470x_set_chan - set the channel
+ */
+static int si470x_set_chan(struct si470x_device *radio, int chan)
+{
+ int retval, i;
+
+ /* start tuning */
+ radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
+ radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
+ retval = si470x_set_register(radio, CHANNEL);
+ if (retval < 0)
+ return retval;
+
+ /* wait till seek operation has completed */
+ i = 0;
+ do {
+ retval = si470x_get_register(radio, STATUSRSSI);
+ if (retval < 0)
+ return retval;
+ } while ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) &&
+ (++i < seek_retries));
+ if (i >= seek_retries)
+ printk(KERN_WARNING DRIVER_NAME
+ ": seek does not finish after %d tries\n", i);
+
+ /* stop tuning */
+ radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
+ return si470x_set_register(radio, CHANNEL);
+}
+
+
+/*
+ * si470x_get_freq - get the frequency
+ */
+static int si470x_get_freq(struct si470x_device *radio)
+{
+ int spacing, band_bottom, chan, freq;
+ int retval;
+
+ /* Spacing (kHz) */
+ switch (space) {
+ /* 0: 200 kHz (USA, Australia) */
+ case 0 : spacing = 0.200 * FREQ_MUL; break;
+ /* 1: 100 kHz (Europe, Japan) */
+ case 1 : spacing = 0.100 * FREQ_MUL; break;
+ /* 2: 50 kHz */
+ default: spacing = 0.050 * FREQ_MUL; break;
+ };
+
+ /* Bottom of Band (MHz) */
+ switch (band) {
+ /* 0: 87.5 - 108 MHz (USA, Europe) */
+ case 0 : band_bottom = 87.5 * FREQ_MUL; break;
+ /* 1: 76 - 108 MHz (Japan wide band) */
+ default: band_bottom = 76 * FREQ_MUL; break;
+ /* 2: 76 - 90 MHz (Japan) */
+ case 2 : band_bottom = 76 * FREQ_MUL; break;
+ };
+
+ /* read channel */
+ retval = si470x_get_register(radio, READCHAN);
+ if (retval < 0)
+ return retval;
+ chan = radio->registers[READCHAN] & READCHAN_READCHAN;
+
+ /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
+ freq = chan * spacing + band_bottom;
+
+ return freq;
+}
+
+
+/*
+ * si470x_set_freq - set the frequency
+ */
+static int si470x_set_freq(struct si470x_device *radio, int freq)
+{
+ int spacing, band_bottom, chan;
+
+ /* Spacing (kHz) */
+ switch (space) {
+ /* 0: 200 kHz (USA, Australia) */
+ case 0 : spacing = 0.200 * FREQ_MUL; break;
+ /* 1: 100 kHz (Europe, Japan) */
+ case 1 : spacing = 0.100 * FREQ_MUL; break;
+ /* 2: 50 kHz */
+ default: spacing = 0.050 * FREQ_MUL; break;
+ };
+
+ /* Bottom of Band (MHz) */
+ switch (band) {
+ /* 0: 87.5 - 108 MHz (USA, Europe) */
+ case 0 : band_bottom = 87.5 * FREQ_MUL; break;
+ /* 1: 76 - 108 MHz (Japan wide band) */
+ default: band_bottom = 76 * FREQ_MUL; break;
+ /* 2: 76 - 90 MHz (Japan) */
+ case 2 : band_bottom = 76 * FREQ_MUL; break;
+ };
+
+ /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
+ chan = (freq - band_bottom) / spacing;
+
+ return si470x_set_chan(radio, chan);
+}
+
+
+/*
+ * si470x_start - switch on radio
+ */
+static int si470x_start(struct si470x_device *radio)
+{
+ int retval;
+
+ /* powercfg */
+ radio->registers[POWERCFG] =
+ POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
+ retval = si470x_set_register(radio, POWERCFG);
+ if (retval < 0)
+ return retval;
+
+ /* sysconfig 1 */
+ radio->registers[SYSCONFIG1] = SYSCONFIG1_DE;
+ retval = si470x_set_register(radio, SYSCONFIG1);
+ if (retval < 0)
+ return retval;
+
+ /* sysconfig 2 */
+ radio->registers[SYSCONFIG2] =
+ (0x3f << 8) | /* SEEKTH */
+ (band << 6) | /* BAND */
+ (space << 4) | /* SPACE */
+ 15; /* VOLUME (max) */
+ retval = si470x_set_register(radio, SYSCONFIG2);
+ if (retval < 0)
+ return retval;
+
+ /* reset last channel */
+ return si470x_set_chan(radio,
+ radio->registers[CHANNEL] & CHANNEL_CHAN);
+}
+
+
+/*
+ * si470x_stop - switch off radio
+ */
+static int si470x_stop(struct si470x_device *radio)
+{
+ int retval;
+
+ /* sysconfig 1 */
+ radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
+ retval = si470x_set_register(radio, SYSCONFIG1);
+ if (retval < 0)
+ return retval;
+
+ /* powercfg */
+ radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
+ /* POWERCFG_ENABLE has to automatically go low */
+ radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE;
+ return si470x_set_register(radio, POWERCFG);
+}
+
+
+/*
+ * si470x_rds_on - switch on rds reception
+ */
+static int si470x_rds_on(struct si470x_device *radio)
+{
+ /* sysconfig 1 */
+ radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
+ return si470x_set_register(radio, SYSCONFIG1);
+}
+
+
+
+/**************************************************************************
+ * RDS Driver Functions
+ **************************************************************************/
+
+/*
+ * si470x_rds - rds processing function
+ */
+static void si470x_rds(struct si470x_device *radio)
+{
+ unsigned char tmpbuf[3];
+ unsigned char blocknum;
+ unsigned char bler; /* rds block errors */
+ unsigned short rds;
+ unsigned int i;
+
+ /* get rds blocks */
+ if (si470x_get_rds_registers(radio) < 0)
+ return;
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) {
+ /* No RDS group ready */
+ return;
+ }
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) {
+ /* RDS decoder not synchronized */
+ return;
+ }
+
+ /* copy four RDS blocks to internal buffer */
+ if (spin_trylock(&radio->lock)) {
+ /* process each rds block */
+ for (blocknum = 0; blocknum < 4; blocknum++) {
+ switch (blocknum) {
+ default:
+ bler = (radio->registers[STATUSRSSI] &
+ STATUSRSSI_BLERA) >> 9;
+ rds = radio->registers[RDSA];
+ break;
+ case 1:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERB) >> 14;
+ rds = radio->registers[RDSB];
+ break;
+ case 2:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERC) >> 12;
+ rds = radio->registers[RDSC];
+ break;
+ case 3:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERD) >> 10;
+ rds = radio->registers[RDSD];
+ break;
+ };
+
+ /* Fill the V4L2 RDS buffer */
+ tmpbuf[0] = rds & 0x00ff; /* LSB */
+ tmpbuf[1] = (rds & 0xff00) >> 8;/* MSB */
+ tmpbuf[2] = blocknum; /* offset name */
+ tmpbuf[2] |= blocknum << 3; /* received offset */
+ if (bler > max_rds_errors)
+ tmpbuf[2] |= 0x80; /* uncorrectable errors */
+ else if (bler > 0)
+ tmpbuf[2] |= 0x40; /* corrected error(s) */
+
+ /* copy RDS block to internal buffer */
+ for (i = 0; i < 3; i++) {
+ radio->buffer[radio->wr_index] = tmpbuf[i];
+ radio->wr_index++;
+ }
+
+ /* wrap write pointer */
+ if (radio->wr_index >= radio->buf_size)
+ radio->wr_index = 0;
+
+ /* check for overflow */
+ if (radio->wr_index == radio->rd_index) {
+ /* increment and wrap read pointer */
+ radio->rd_index += 3;
+ if (radio->rd_index >= radio->buf_size)
+ radio->rd_index = 0;
+ }
+ }
+ spin_unlock(&radio->lock);
+ }
+
+ /* wake up read queue */
+ if (radio->wr_index != radio->rd_index)
+ wake_up_interruptible(&radio->read_queue);
+}
+
+
+/*
+ * si470x_timer - rds timer function
+ */
+static void si470x_timer(unsigned long data)
+{
+ struct si470x_device *radio = (struct si470x_device *) data;
+
+ schedule_work(&radio->work);
+}
+
+
+/*
+ * si470x_work - rds work function
+ */
+static void si470x_work(struct work_struct *work)
+{
+ struct si470x_device *radio = container_of(work, struct si470x_device,
+ work);
+
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ return;
+
+ si470x_rds(radio);
+ mod_timer(&radio->timer, jiffies + msecs_to_jiffies(rds_poll_time));
+}
+
+
+
+/**************************************************************************
+ * File Operations Interface
+ **************************************************************************/
+
+/*
+ * si470x_fops_read - read RDS data
+ */
+static ssize_t si470x_fops_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = 0;
+ unsigned int block_count = 0;
+
+ /* switch on rds reception */
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
+ si470x_rds_on(radio);
+ schedule_work(&radio->work);
+ }
+
+ /* block if no new data available */
+ while (radio->wr_index == radio->rd_index) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+ interruptible_sleep_on(&radio->read_queue);
+ }
+
+ /* calculate block count from byte count */
+ count /= 3;
+
+ /* copy RDS block out of internal buffer and to user buffer */
+ if (spin_trylock(&radio->lock)) {
+ while (block_count < count) {
+ if (radio->rd_index == radio->wr_index)
+ break;
+
+ /* always transfer rds complete blocks */
+ if (copy_to_user(buf,
+ &radio->buffer[radio->rd_index], 3))
+ /* retval = -EFAULT; */
+ break;
+
+ /* increment and wrap read pointer */
+ radio->rd_index += 3;
+ if (radio->rd_index >= radio->buf_size)
+ radio->rd_index = 0;
+
+ /* increment counters */
+ block_count++;
+ buf += 3;
+ retval += 3;
+ }
+
+ spin_unlock(&radio->lock);
+ }
+
+ return retval;
+}
+
+
+/*
+ * si470x_fops_poll - poll RDS data
+ */
+static unsigned int si470x_fops_poll(struct file *file,
+ struct poll_table_struct *pts)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ /* switch on rds reception */
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
+ si470x_rds_on(radio);
+ schedule_work(&radio->work);
+ }
+
+ poll_wait(file, &radio->read_queue, pts);
+
+ if (radio->rd_index != radio->wr_index)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+
+/*
+ * si470x_fops_open - file open
+ */
+static int si470x_fops_open(struct inode *inode, struct file *file)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ radio->users++;
+ if (radio->users == 1)
+ return si470x_start(radio);
+
+ return 0;
+}
+
+
+/*
+ * si470x_fops_release - file release
+ */
+static int si470x_fops_release(struct inode *inode, struct file *file)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ if (!radio)
+ return -ENODEV;
+
+ radio->users--;
+ if (radio->users == 0) {
+ /* stop rds reception */
+ del_timer_sync(&radio->timer);
+ flush_scheduled_work();
+
+ /* cancel read processes */
+ wake_up_interruptible(&radio->read_queue);
+
+ return si470x_stop(radio);
+ }
+
+ return 0;
+}
+
+
+/*
+ * si470x_fops - file operations interface
+ */
+static const struct file_operations si470x_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = si470x_fops_read,
+ .poll = si470x_fops_poll,
+ .ioctl = video_ioctl2,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .open = si470x_fops_open,
+ .release = si470x_fops_release,
+};
+
+
+
+/**************************************************************************
+ * Video4Linux Interface
+ **************************************************************************/
+
+/*
+ * si470x_v4l2_queryctrl - query control
+ */
+static struct v4l2_queryctrl si470x_v4l2_queryctrl[] = {
+/* HINT: the disabled controls are only here to satify kradio and such apps */
+ {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 15,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BASS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_AUDIO_LOUDNESS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+};
+
+
+/*
+ * si470x_vidioc_querycap - query device capabilities
+ */
+static int si470x_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
+{
+ strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+ strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+ sprintf(capability->bus_info, "USB");
+ capability->version = DRIVER_VERSION;
+ capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_g_input - get input
+ */
+static int si470x_vidioc_g_input(struct file *filp, void *priv,
+ unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_s_input - set input
+ */
+static int si470x_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_queryctrl - enumerate control items
+ */
+static int si470x_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(si470x_v4l2_queryctrl); i++) {
+ if (qc->id && qc->id == si470x_v4l2_queryctrl[i].id) {
+ memcpy(qc, &(si470x_v4l2_queryctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+
+/*
+ * si470x_vidioc_g_ctrl - get the value of a control
+ */
+static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value = radio->registers[SYSCONFIG2] &
+ SYSCONFIG2_VOLUME;
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = ((radio->registers[POWERCFG] &
+ POWERCFG_DMUTE) == 0) ? 1 : 0;
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_s_ctrl - set the value of a control
+ */
+static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
+ radio->registers[SYSCONFIG2] |= ctrl->value;
+ return si470x_set_register(radio, SYSCONFIG2);
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value == 1)
+ radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
+ else
+ radio->registers[POWERCFG] |= POWERCFG_DMUTE;
+ return si470x_set_register(radio, POWERCFG);
+ }
+
+ return -EINVAL;
+}
+
+
+/*
+ * si470x_vidioc_g_audio - get audio attributes
+ */
+static int si470x_vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ if (audio->index > 1)
+ return -EINVAL;
+
+ strcpy(audio->name, "Radio");
+ audio->capability = V4L2_AUDCAP_STEREO;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_s_audio - set audio attributes
+ */
+static int si470x_vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ if (audio->index != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_g_tuner - get tuner attributes
+ */
+static int si470x_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ int retval;
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ if (tuner->index > 0)
+ return -EINVAL;
+
+ /* read status rssi */
+ retval = si470x_get_register(radio, STATUSRSSI);
+ if (retval < 0)
+ return retval;
+
+ strcpy(tuner->name, "FM");
+ tuner->type = V4L2_TUNER_RADIO;
+ switch (band) {
+ /* 0: 87.5 - 108 MHz (USA, Europe, default) */
+ default:
+ tuner->rangelow = 87.5 * FREQ_MUL;
+ tuner->rangehigh = 108 * FREQ_MUL;
+ break;
+ /* 1: 76 - 108 MHz (Japan wide band) */
+ case 1 :
+ tuner->rangelow = 76 * FREQ_MUL;
+ tuner->rangehigh = 108 * FREQ_MUL;
+ break;
+ /* 2: 76 - 90 MHz (Japan) */
+ case 2 :
+ tuner->rangelow = 76 * FREQ_MUL;
+ tuner->rangehigh = 90 * FREQ_MUL;
+ break;
+ };
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ tuner->capability = V4L2_TUNER_CAP_LOW;
+
+ /* Stereo indicator == Stereo (instead of Mono) */
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 1)
+ tuner->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ tuner->audmode = V4L2_TUNER_MODE_MONO;
+
+ /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
+ tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI)
+ * 0x0101;
+
+ /* automatic frequency control: -1: freq to low, 1 freq to high */
+ tuner->afc = 0;
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_s_tuner - set tuner attributes
+ */
+static int si470x_vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ if (tuner->index > 0)
+ return -EINVAL;
+
+ if (tuner->audmode == V4L2_TUNER_MODE_MONO)
+ radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */
+ else
+ radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
+
+ return si470x_set_register(radio, POWERCFG);
+}
+
+
+/*
+ * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
+ */
+static int si470x_vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ freq->type = V4L2_TUNER_RADIO;
+ freq->frequency = si470x_get_freq(radio);
+
+ return 0;
+}
+
+
+/*
+ * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
+ */
+static int si470x_vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+
+ if (freq->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+
+ return si470x_set_freq(radio, freq->frequency);
+}
+
+
+/*
+ * si470x_viddev_tamples - video device interface
+ */
+static struct video_device si470x_viddev_template = {
+ .fops = &si470x_fops,
+ .name = DRIVER_NAME,
+ .type = VID_TYPE_TUNER,
+ .release = video_device_release,
+ .vidioc_querycap = si470x_vidioc_querycap,
+ .vidioc_g_input = si470x_vidioc_g_input,
+ .vidioc_s_input = si470x_vidioc_s_input,
+ .vidioc_queryctrl = si470x_vidioc_queryctrl,
+ .vidioc_g_ctrl = si470x_vidioc_g_ctrl,
+ .vidioc_s_ctrl = si470x_vidioc_s_ctrl,
+ .vidioc_g_audio = si470x_vidioc_g_audio,
+ .vidioc_s_audio = si470x_vidioc_s_audio,
+ .vidioc_g_tuner = si470x_vidioc_g_tuner,
+ .vidioc_s_tuner = si470x_vidioc_s_tuner,
+ .vidioc_g_frequency = si470x_vidioc_g_frequency,
+ .vidioc_s_frequency = si470x_vidioc_s_frequency,
+ .owner = THIS_MODULE,
+};
+
+
+
+/**************************************************************************
+ * USB Interface
+ **************************************************************************/
+
+/*
+ * si470x_usb_driver_probe - probe for the device
+ */
+static int si470x_usb_driver_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct si470x_device *radio;
+
+ /* memory and interface allocations */
+ radio = kmalloc(sizeof(struct si470x_device), GFP_KERNEL);
+ if (!radio)
+ return -ENOMEM;
+ radio->videodev = video_device_alloc();
+ if (!radio->videodev) {
+ kfree(radio);
+ return -ENOMEM;
+ }
+ memcpy(radio->videodev, &si470x_viddev_template,
+ sizeof(si470x_viddev_template));
+ radio->users = 0;
+ radio->usbdev = interface_to_usbdev(intf);
+ video_set_drvdata(radio->videodev, radio);
+ if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) {
+ printk(KERN_WARNING DRIVER_NAME
+ ": Could not register video device\n");
+ video_device_release(radio->videodev);
+ kfree(radio);
+ return -EIO;
+ }
+ usb_set_intfdata(intf, radio);
+
+ /* show some infos about the specific device */
+ if (si470x_get_all_registers(radio) < 0) {
+ video_device_release(radio->videodev);
+ kfree(radio);
+ return -EIO;
+ }
+ printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4x ChipID=0x%4.4x\n",
+ radio->registers[DEVICEID], radio->registers[CHIPID]);
+
+ /* check if firmware is current */
+ if ((radio->registers[CHIPID] & CHIPID_FIRMWARE)
+ < RADIO_SW_VERSION_CURRENT)
+ printk(KERN_WARNING DRIVER_NAME
+ ": This driver is known to work with chip version %d, "
+ "but the device has firmware %d.\n"
+ DRIVER_NAME
+ "If you have some trouble using this driver, please "
+ "report to V4L ML at video4linux-list@redhat.com\n",
+ radio->registers[CHIPID] & CHIPID_FIRMWARE,
+ RADIO_SW_VERSION_CURRENT);
+
+ /* set initial frequency */
+ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
+
+ /* rds initialization */
+ radio->buf_size = rds_buf * 3;
+ radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
+ if (!radio->buffer) {
+ video_device_release(radio->videodev);
+ kfree(radio);
+ return -ENOMEM;
+ }
+ radio->wr_index = 0;
+ radio->rd_index = 0;
+ init_waitqueue_head(&radio->read_queue);
+
+ /* prepare polling via eventd */
+ INIT_WORK(&radio->work, si470x_work);
+ init_timer(&radio->timer);
+ radio->timer.function = si470x_timer;
+ radio->timer.data = (unsigned long) radio;
+
+ return 0;
+}
+
+
+/*
+ * si470x_usb_driver_disconnect - disconnect the device
+ */
+static void si470x_usb_driver_disconnect(struct usb_interface *intf)
+{
+ struct si470x_device *radio = usb_get_intfdata(intf);
+
+ del_timer_sync(&radio->timer);
+ flush_scheduled_work();
+
+ usb_set_intfdata(intf, NULL);
+ if (radio) {
+ video_unregister_device(radio->videodev);
+ kfree(radio->buffer);
+ kfree(radio);
+ }
+}
+
+
+/*
+ * si470x_usb_driver - usb driver interface
+ */
+static struct usb_driver si470x_usb_driver = {
+ .name = DRIVER_NAME,
+ .probe = si470x_usb_driver_probe,
+ .disconnect = si470x_usb_driver_disconnect,
+ .id_table = si470x_usb_driver_id_table,
+};
+
+
+
+/**************************************************************************
+ * Module Interface
+ **************************************************************************/
+
+/*
+ * si470x_module_init - module init
+ */
+static int __init si470x_module_init(void)
+{
+ printk(KERN_INFO DRIVER_DESC "\n");
+ return usb_register(&si470x_usb_driver);
+}
+
+
+/*
+ * si470x_module_exit - module exit
+ */
+static void __exit si470x_module_exit(void)
+{
+ usb_deregister(&si470x_usb_driver);
+}
+
+
+module_init(si470x_module_init);
+module_exit(si470x_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION("1.0.4");
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index c9f14bfc8544..a2e8987a6195 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -45,7 +45,7 @@ comment "Audio decoders"
config VIDEO_TVAUDIO
tristate "Simple audio decoder chips"
- depends on VIDEO_V4L1 && I2C
+ depends on VIDEO_V4L2 && I2C
---help---
Support for several audio decoder chips found on some bt8xx boards:
Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300,
@@ -57,7 +57,7 @@ config VIDEO_TVAUDIO
config VIDEO_TDA7432
tristate "Philips TDA7432 audio processor"
- depends on VIDEO_V4L1 && I2C
+ depends on VIDEO_V4L2 && I2C
---help---
Support for tda7432 audio decoder chip found on some bt8xx boards.
@@ -75,7 +75,7 @@ config VIDEO_TDA9840
config VIDEO_TDA9875
tristate "Philips TDA9875 audio processor"
- depends on VIDEO_V4L1 && I2C
+ depends on VIDEO_V4L2 && I2C
---help---
Support for tda9875 audio decoder chip found on some bt8xx boards.
@@ -109,9 +109,19 @@ config VIDEO_MSP3400
To compile this driver as a module, choose M here: the
module will be called msp3400.
+config VIDEO_CS5345
+ tristate "Cirrus Logic CS5345 audio ADC"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the Cirrus Logic CS5345 24-bit, 192 kHz
+ stereo A/D converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cs5345.
+
config VIDEO_CS53L32A
tristate "Cirrus Logic CS53L32A audio ADC"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Cirrus Logic CS53L32A low voltage
stereo A/D converter.
@@ -119,6 +129,15 @@ config VIDEO_CS53L32A
To compile this driver as a module, choose M here: the
module will be called cs53l32a.
+config VIDEO_M52790
+ tristate "Mitsubishi M52790 A/V switch"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the Mitsubishi M52790 A/V switch.
+
+ To compile this driver as a module, choose M here: the
+ module will be called m52790.
+
config VIDEO_TLV320AIC23B
tristate "Texas Instruments TLV320AIC23B audio codec"
depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
@@ -130,7 +149,7 @@ config VIDEO_TLV320AIC23B
config VIDEO_WM8775
tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Wolfson Microelectronics WM8775 high
performance stereo A/D Converter with a 4 channel input mixer.
@@ -140,7 +159,7 @@ config VIDEO_WM8775
config VIDEO_WM8739
tristate "Wolfson Microelectronics WM8739 stereo audio ADC"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Wolfson Microelectronics WM8739
stereo A/D Converter.
@@ -244,7 +263,7 @@ config VIDEO_SAA7114
config VIDEO_SAA711X
tristate "Philips SAA7113/4/5 video decoders"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Philips SAA7113/4/5 video decoders.
@@ -300,7 +319,7 @@ comment "Video encoders"
config VIDEO_SAA7127
tristate "Philips SAA7127/9 digital video encoders"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Philips SAA7127/9 digital video encoders.
@@ -338,7 +357,7 @@ comment "Video improvement chips"
config VIDEO_UPD64031A
tristate "NEC Electronics uPD64031A Ghost Reduction"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the NEC Electronics uPD64031A Ghost Reduction
video chip. It is most often found in NTSC TV cards made for
@@ -350,7 +369,7 @@ config VIDEO_UPD64031A
config VIDEO_UPD64083
tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the NEC Electronics uPD64083 3-Dimensional Y/C
separation video chip. It is used to improve the quality of
@@ -802,6 +821,19 @@ config USB_ZR364XX
To compile this driver as a module, choose M here: the
module will be called zr364xx.
+config USB_STKWEBCAM
+ tristate "USB Syntek DC1125 Camera support"
+ depends on VIDEO_V4L2 && EXPERIMENTAL
+ ---help---
+ Say Y here if you want to use this type of camera.
+ Supported devices are typically found in some Asus laptops,
+ with USB id 174f:a311 and 05e1:0501. Other Syntek cameras
+ may be supported by the stk11xx driver, from which this is
+ derived, see http://stk11xx.sourceforge.net
+
+ To compile this driver as a module, choose M here: the
+ module will be called stkwebcam.
+
endif # V4L_USB_DRIVERS
endif # VIDEO_CAPTURE_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index b5a064163e03..28ddd146c1c5 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -4,10 +4,12 @@
zr36067-objs := zoran_procfs.o zoran_device.o \
zoran_driver.o zoran_card.o
-tuner-objs := tuner-core.o tuner-types.o tda9887.o
+tuner-objs := tuner-core.o tuner-types.o
msp3400-objs := msp3400-driver.o msp3400-kthreads.o
+stkwebcam-objs := stk-webcam.o stk-sensor.o
+
obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o compat_ioctl32.o \
v4l2-int-device.o
@@ -66,7 +68,9 @@ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
+obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
+obj-$(CONFIG_VIDEO_M52790) += m52790.o
obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o
obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
@@ -81,11 +85,13 @@ obj-$(CONFIG_TUNER_3036) += tuner-3036.o
obj-$(CONFIG_VIDEO_TUNER) += tuner.o
+obj-$(CONFIG_TUNER_XC2028) += tuner-xc2028.o
obj-$(CONFIG_TUNER_SIMPLE) += tuner-simple.o
obj-$(CONFIG_TUNER_MT20XX) += mt20xx.o
obj-$(CONFIG_TUNER_TDA8290) += tda8290.o
obj-$(CONFIG_TUNER_TEA5767) += tea5767.o
obj-$(CONFIG_TUNER_TEA5761) += tea5761.o
+obj-$(CONFIG_TUNER_TDA9887) += tda9887.o
obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
@@ -112,6 +118,7 @@ obj-$(CONFIG_USB_SE401) += se401.o
obj-$(CONFIG_USB_STV680) += stv680.o
obj-$(CONFIG_USB_W9968CF) += w9968cf.o
obj-$(CONFIG_USB_ZR364XX) += zr364xx.o
+obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o
obj-$(CONFIG_USB_SN9C102) += sn9c102/
obj-$(CONFIG_USB_ET61X251) += et61x251/
@@ -129,3 +136,4 @@ obj-$(CONFIG_VIDEO_VIVI) += vivi.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885/
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig
index 2ca162b390a2..cfc822bb502a 100644
--- a/drivers/media/video/bt8xx/Kconfig
+++ b/drivers/media/video/bt8xx/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_BT848
tristate "BT848 Video For Linux"
- depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L1
+ depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2 && INPUT
select I2C_ALGOBIT
select FW_LOADER
select VIDEO_BTCX
diff --git a/drivers/media/video/bt8xx/Makefile b/drivers/media/video/bt8xx/Makefile
index a096a03418aa..924d216d9570 100644
--- a/drivers/media/video/bt8xx/Makefile
+++ b/drivers/media/video/bt8xx/Makefile
@@ -4,7 +4,7 @@
bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \
bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o \
- bttv-input.o
+ bttv-input.o bttv-audio-hook.o
obj-$(CONFIG_VIDEO_BT848) += bttv.o
diff --git a/drivers/media/video/bt8xx/bttv-audio-hook.c b/drivers/media/video/bt8xx/bttv-audio-hook.c
new file mode 100644
index 000000000000..2364d16586b3
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv-audio-hook.c
@@ -0,0 +1,382 @@
+/*
+ * Handlers for board audio hooks, splitted from bttv-cards
+ *
+ * Copyright (c) 2006 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License
+ */
+
+#include "bttv-audio-hook.h"
+
+#include <linux/delay.h>
+
+/* ----------------------------------------------------------------------- */
+/* winview */
+
+void winview_volume(struct bttv *btv, __u16 volume)
+{
+ /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */
+ int bits_out, loops, vol, data;
+
+ /* 32 levels logarithmic */
+ vol = 32 - ((volume>>11));
+ /* units */
+ bits_out = (PT2254_DBS_IN_2>>(vol%5));
+ /* tens */
+ bits_out |= (PT2254_DBS_IN_10>>(vol/5));
+ bits_out |= PT2254_L_CHANNEL | PT2254_R_CHANNEL;
+ data = gpio_read();
+ data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA|
+ WINVIEW_PT2254_STROBE);
+ for (loops = 17; loops >= 0 ; loops--) {
+ if (bits_out & (1<<loops))
+ data |= WINVIEW_PT2254_DATA;
+ else
+ data &= ~WINVIEW_PT2254_DATA;
+ gpio_write(data);
+ udelay(5);
+ data |= WINVIEW_PT2254_CLK;
+ gpio_write(data);
+ udelay(5);
+ data &= ~WINVIEW_PT2254_CLK;
+ gpio_write(data);
+ }
+ data |= WINVIEW_PT2254_STROBE;
+ data &= ~WINVIEW_PT2254_DATA;
+ gpio_write(data);
+ udelay(10);
+ data &= ~WINVIEW_PT2254_STROBE;
+ gpio_write(data);
+}
+
+/* ----------------------------------------------------------------------- */
+/* mono/stereo control for various cards (which don't use i2c chips but */
+/* connect something to the GPIO pins */
+
+void gvbctv3pci_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int con = 0;
+
+ if (set) {
+ gpio_inout(0x300, 0x300);
+ if (t->audmode & V4L2_TUNER_MODE_LANG1)
+ con = 0x000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ con = 0x300;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ con = 0x200;
+/* if (t->audmode & V4L2_TUNER_MODE_MONO)
+ * con = 0x100; */
+ gpio_bits(0x300, con);
+ } else {
+ t->audmode = V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int val, con;
+
+ if (btv->radio_user)
+ return;
+
+ val = gpio_read();
+ if (set) {
+ con = 0x000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG1) {
+ /* LANG1 + LANG2 */
+ con = 0x100;
+ }
+ else {
+ /* LANG2 */
+ con = 0x300;
+ }
+ }
+ if (con != (val & 0x300)) {
+ gpio_bits(0x300, con);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"gvbctv5pci");
+ }
+ } else {
+ switch (val & 0x70) {
+ case 0x10:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x30:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x50:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1;
+ break;
+ case 0x60:
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ break;
+ case 0x70:
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ break;
+ default:
+ t->rxsubchans = V4L2_TUNER_SUB_MONO |
+ V4L2_TUNER_SUB_STEREO |
+ V4L2_TUNER_SUB_LANG1 |
+ V4L2_TUNER_SUB_LANG2;
+ }
+ t->audmode = V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * Mario Medina Nussbaum <medisoft@alohabbs.org.mx>
+ * I discover that on BT848_GPIO_DATA address a byte 0xcce enable stereo,
+ * 0xdde enables mono and 0xccd enables sap
+ *
+ * Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * P.S.: At least mask in line above is wrong - GPIO pins 3,2 select
+ * input/output sound connection, so both must be set for output mode.
+ *
+ * Looks like it's needed only for the "tvphone", the "tvphone 98"
+ * handles this with a tda9840
+ *
+ */
+
+void avermedia_tvphone_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ int val = 0;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */
+ val = 0x02;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ val = 0x01;
+ if (val) {
+ gpio_bits(0x03,val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"avermedia");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1;
+ return;
+ }
+}
+
+
+void avermedia_tv_stereo_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ int val = 0;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */
+ val = 0x01;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* STEREO */
+ val = 0x02;
+ btaor(val, ~0x03, BT848_GPIO_DATA);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"avermedia");
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ return;
+ }
+}
+
+/* Lifetec 9415 handling */
+
+void lt9415_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ int val = 0;
+
+ if (gpio_read() & 0x4000) {
+ t->audmode = V4L2_TUNER_MODE_MONO;
+ return;
+ }
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* A2 SAP */
+ val = 0x0080;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* A2 stereo */
+ val = 0x0880;
+ if ((t->audmode & V4L2_TUNER_MODE_LANG1) ||
+ (t->audmode & V4L2_TUNER_MODE_MONO))
+ val = 0;
+ gpio_bits(0x0880, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"lt9415");
+ } else {
+ /* autodetect doesn't work with this card :-( */
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ return;
+ }
+}
+
+/* TDA9821 on TerraTV+ Bt848, Bt878 */
+void terratv_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int con = 0;
+
+ if (set) {
+ gpio_inout(0x180000,0x180000);
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ con = 0x080000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ con = 0x180000;
+ gpio_bits(0x180000, con);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"terratv");
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+
+void winfast2000_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned long val = 0;
+
+ if (set) {
+ /*btor (0xc32000, BT848_GPIO_OUT_EN);*/
+ if (t->audmode & V4L2_TUNER_MODE_MONO) /* Mono */
+ val = 0x420000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG1) /* Mono */
+ val = 0x420000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */
+ val = 0x410000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* Stereo */
+ val = 0x020000;
+ if (val) {
+ gpio_bits(0x430000, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"winfast2000");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * Dariusz Kowalewski <darekk@automex.pl>
+ * sound control for Prolink PV-BT878P+9B (PixelView PlayTV Pro FM+NICAM
+ * revision 9B has on-board TDA9874A sound decoder).
+ *
+ * Note: There are card variants without tda9874a. Forcing the "stereo sound route"
+ * will mute this cards.
+ */
+void pvbt878p9b_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int val = 0;
+
+ if (btv->radio_user)
+ return;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_MONO) {
+ val = 0x01;
+ }
+ if ((t->audmode & (V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2))
+ || (t->audmode & V4L2_TUNER_MODE_STEREO)) {
+ val = 0x02;
+ }
+ if (val) {
+ gpio_bits(0x03,val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"pvbt878p9b");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * Dariusz Kowalewski <darekk@automex.pl>
+ * sound control for FlyVideo 2000S (with tda9874 decoder)
+ * based on pvbt878p9b_audio() - this is not tested, please fix!!!
+ */
+void fv2000s_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int val = 0xffff;
+
+ if (btv->radio_user)
+ return;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_MONO) {
+ val = 0x0000;
+ }
+ if ((t->audmode & (V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2))
+ || (t->audmode & V4L2_TUNER_MODE_STEREO)) {
+ val = 0x1080; /*-dk-???: 0x0880, 0x0080, 0x1800 ... */
+ }
+ if (val != 0xffff) {
+ gpio_bits(0x1800, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"fv2000s");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * sound control for Canopus WinDVR PCI
+ * Masaki Suzuki <masaki@btree.org>
+ */
+void windvr_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned long val = 0;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_MONO)
+ val = 0x040000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG1)
+ val = 0;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ val = 0x100000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ val = 0;
+ if (val) {
+ gpio_bits(0x140000, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"windvr");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * sound control for AD-TVK503
+ * Hiroshi Takekawa <sian@big.or.jp>
+ */
+void adtvk503_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int con = 0xffffff;
+
+ /* btaor(0x1e0000, ~0x1e0000, BT848_GPIO_OUT_EN); */
+
+ if (set) {
+ /* btor(***, BT848_GPIO_OUT_EN); */
+ if (t->audmode & V4L2_TUNER_MODE_LANG1)
+ con = 0x00000000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ con = 0x00180000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ con = 0x00000000;
+ if (t->audmode & V4L2_TUNER_MODE_MONO)
+ con = 0x00060000;
+ if (con != 0xffffff) {
+ gpio_bits(0x1e0000,con);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv, "adtvk503");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
diff --git a/drivers/media/video/bt8xx/bttv-audio-hook.h b/drivers/media/video/bt8xx/bttv-audio-hook.h
new file mode 100644
index 000000000000..159d07adeff8
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv-audio-hook.h
@@ -0,0 +1,23 @@
+/*
+ * Handlers for board audio hooks, splitted from bttv-cards
+ *
+ * Copyright (c) 2006 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License
+ */
+
+#include "bttvp.h"
+
+void winview_volume (struct bttv *btv, __u16 volume);
+
+void lt9415_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void avermedia_tvphone_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void avermedia_tv_stereo_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void terratv_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void gvbctv3pci_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void winfast2000_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void pvbt878p9b_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void fv2000s_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void windvr_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void adtvk503_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 585d1ef95afd..63a47cd4c161 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -39,6 +39,7 @@
#include "bttvp.h"
#include <media/v4l2-common.h>
#include <media/tvaudio.h>
+#include "bttv-audio-hook.h"
/* fwd decl */
static void boot_msp34xx(struct bttv *btv, int pin);
@@ -50,20 +51,6 @@ static void modtec_eeprom(struct bttv *btv);
static void init_PXC200(struct bttv *btv);
static void init_RTV24(struct bttv *btv);
-static void winview_audio(struct bttv *btv, struct video_audio *v, int set);
-static void lt9415_audio(struct bttv *btv, struct video_audio *v, int set);
-static void avermedia_tvphone_audio(struct bttv *btv, struct video_audio *v,
- int set);
-static void avermedia_tv_stereo_audio(struct bttv *btv, struct video_audio *v,
- int set);
-static void terratv_audio(struct bttv *btv, struct video_audio *v, int set);
-static void gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set);
-static void gvbctv5pci_audio(struct bttv *btv, struct video_audio *v, int set);
-static void winfast2000_audio(struct bttv *btv, struct video_audio *v, int set);
-static void pvbt878p9b_audio(struct bttv *btv, struct video_audio *v, int set);
-static void fv2000s_audio(struct bttv *btv, struct video_audio *v, int set);
-static void windvr_audio(struct bttv *btv, struct video_audio *v, int set);
-static void adtvk503_audio(struct bttv *btv, struct video_audio *v, int set);
static void rv605_muxsel(struct bttv *btv, unsigned int input);
static void eagle_muxsel(struct bttv *btv, unsigned int input);
static void xguard_muxsel(struct bttv *btv, unsigned int input);
@@ -427,7 +414,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = avermedia_tvphone_audio,
+ .audio_mode_gpio= avermedia_tvphone_audio,
.has_remote = 1,
},
[BTTV_BOARD_MATRIX_VISION] = {
@@ -539,7 +526,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_PAL,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = avermedia_tv_stereo_audio,
+ .audio_mode_gpio= avermedia_tv_stereo_audio,
.no_gpioirq = 1,
},
[BTTV_BOARD_VHX] = {
@@ -604,7 +591,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = winview_audio,
+ .volume_gpio = winview_volume,
.has_radio = 1,
},
[BTTV_BOARD_AVEC_INTERCAP] = {
@@ -728,7 +715,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_PAL,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = terratv_audio,
+ .audio_mode_gpio= terratv_audio,
},
[BTTV_BOARD_HAUPPAUG_WCAM] = {
.name = "Hauppauge WinCam newer (bt878)",
@@ -776,7 +763,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_PAL,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = terratv_audio,
+ .audio_mode_gpio= terratv_audio,
/* GPIO wiring:
External 20 pin connector (for Active Radio Upgrade board)
gpio00: i2c-sda
@@ -915,7 +902,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_PAL, /* default for now, gpio reads BFFF06 for Pal bg+dk */
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = winfast2000_audio,
+ .audio_mode_gpio= winfast2000_audio,
.has_remote = 1,
},
[BTTV_BOARD_CHRONOS_VS2] = {
@@ -1035,7 +1022,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.has_radio = 1,
- .audio_hook = avermedia_tvphone_audio,
+ .audio_mode_gpio= avermedia_tvphone_audio,
},
[BTTV_BOARD_PV951] = {
.name = "ProVideo PV951", /* pic16c54 */
@@ -1167,7 +1154,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_ALPS_TSHC6_NTSC,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = gvbctv3pci_audio,
+ .audio_mode_gpio= gvbctv3pci_audio,
},
[BTTV_BOARD_PXELVWPLTVPAK] = {
.name = "Prolink PV-BT878P+4E / PixelView PlayTV PAK / Lenco MXTV-9578 CP",
@@ -1472,7 +1459,7 @@ struct tvcard bttv_tvcards[] = {
/* -dk-???: set mute=0x1800 for tda9874h daughterboard */
.gpiomux = { 0x0000,0x0800,0x1000,0x1000 },
.gpiomute = 0x1800,
- .audio_hook = fv2000s_audio,
+ .audio_mode_gpio= fv2000s_audio,
.no_msp34xx = 1,
.no_tda9875 = 1,
.needs_tvaudio = 1,
@@ -1513,7 +1500,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_SHARP_2U5JF5540_NTSC,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = gvbctv3pci_audio,
+ .audio_mode_gpio= gvbctv3pci_audio,
},
/* ---- card 0x44 ---------------------------------- */
@@ -1632,7 +1619,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_PAL,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = pvbt878p9b_audio, /* Note: not all cards have stereo */
+ .audio_mode_gpio= pvbt878p9b_audio, /* Note: not all cards have stereo */
.has_radio = 1, /* Note: not all cards have radio */
.has_remote = 1,
/* GPIO wiring:
@@ -1710,7 +1697,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_NTSC,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = windvr_audio,
+ .audio_mode_gpio= windvr_audio,
},
[BTTV_BOARD_GRANDTEC_MULTI] = {
.name = "GrandTec Multi Capture Card (Bt878)",
@@ -1807,7 +1794,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_NTSC_M,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = gvbctv5pci_audio,
+ .audio_mode_gpio= gvbctv5pci_audio,
.has_radio = 1,
},
[BTTV_BOARD_OSPREY1x0] = {
@@ -2106,7 +2093,7 @@ struct tvcard bttv_tvcards[] = {
.tuner_type = TUNER_PHILIPS_NTSC,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_hook = adtvk503_audio,
+ .audio_mode_gpio= adtvk503_audio,
},
/* ---- card 0x64 ---------------------------------- */
@@ -3173,8 +3160,8 @@ static void flyvideo_gpio(struct bttv *btv)
/* LR90 Audio Routing is done by 2 hef4052, so Audio_Mask has 4 bits: 0x001c80
* LR26/LR50 only has 1 hef4052, Audio_Mask 0x000c00
* Audio options: from tuner, from tda9821/tda9821(mono,stereo,sap), from tda9874, ext., mute */
- if(has_tda9820_tda9821) btv->audio_hook = lt9415_audio;
- /* todo: if(has_tda9874) btv->audio_hook = fv2000s_audio; */
+ if(has_tda9820_tda9821) btv->audio_mode_gpio = lt9415_audio;
+ /* todo: if(has_tda9874) btv->audio_mode_gpio = fv2000s_audio; */
}
static int miro_tunermap[] = { 0,6,2,3, 4,5,6,0, 3,0,4,5, 5,2,16,1,
@@ -3574,8 +3561,12 @@ void __devinit bttv_init_card2(struct bttv *btv)
}
if (btv->tda9887_conf) {
- bttv_call_i2c_clients(btv, TDA9887_SET_CONFIG,
- &btv->tda9887_conf);
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &btv->tda9887_conf;
+
+ bttv_call_i2c_clients(btv, TUNER_SET_CONFIG, &tda9887_cfg);
}
btv->svhs = bttv_tvcards[btv->c.type].svhs;
@@ -3590,8 +3581,10 @@ void __devinit bttv_init_card2(struct bttv *btv)
btv->has_remote=1;
if (!bttv_tvcards[btv->c.type].no_gpioirq)
btv->gpioirq=1;
- if (bttv_tvcards[btv->c.type].audio_hook)
- btv->audio_hook=bttv_tvcards[btv->c.type].audio_hook;
+ if (bttv_tvcards[btv->c.type].volume_gpio)
+ btv->volume_gpio=bttv_tvcards[btv->c.type].volume_gpio;
+ if (bttv_tvcards[btv->c.type].audio_mode_gpio)
+ btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio;
if (bttv_tvcards[btv->c.type].digital_mode == DIGITAL_MODE_CAMERA) {
/* detect Bt832 chip for quartzsight digital camera */
@@ -3950,7 +3943,7 @@ static void __devinit avermedia_eeprom(struct bttv *btv)
void bttv_tda9880_setnorm(struct bttv *btv, int norm)
{
/* fix up our card entry */
- if(norm==VIDEO_MODE_NTSC) {
+ if(norm==V4L2_STD_NTSC) {
bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomux[TVAUDIO_INPUT_TUNER]=0x957fff;
bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomute=0x957fff;
bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomux[TVAUDIO_INPUT_TUNER]=0x957fff;
@@ -4319,387 +4312,6 @@ void tea5757_set_freq(struct bttv *btv, unsigned short freq)
tea5757_write(btv, 5 * freq + 0x358); /* add 10.7MHz (see docs) */
}
-
-/* ----------------------------------------------------------------------- */
-/* winview */
-
-static void winview_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */
- int bits_out, loops, vol, data;
-
- if (!set) {
- /* Fixed by Leandro Lucarella <luca@linuxmendoza.org.ar (07/31/01) */
- v->flags |= VIDEO_AUDIO_VOLUME;
- return;
- }
-
- /* 32 levels logarithmic */
- vol = 32 - ((v->volume>>11));
- /* units */
- bits_out = (PT2254_DBS_IN_2>>(vol%5));
- /* tens */
- bits_out |= (PT2254_DBS_IN_10>>(vol/5));
- bits_out |= PT2254_L_CHANNEL | PT2254_R_CHANNEL;
- data = gpio_read();
- data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA|
- WINVIEW_PT2254_STROBE);
- for (loops = 17; loops >= 0 ; loops--) {
- if (bits_out & (1<<loops))
- data |= WINVIEW_PT2254_DATA;
- else
- data &= ~WINVIEW_PT2254_DATA;
- gpio_write(data);
- udelay(5);
- data |= WINVIEW_PT2254_CLK;
- gpio_write(data);
- udelay(5);
- data &= ~WINVIEW_PT2254_CLK;
- gpio_write(data);
- }
- data |= WINVIEW_PT2254_STROBE;
- data &= ~WINVIEW_PT2254_DATA;
- gpio_write(data);
- udelay(10);
- data &= ~WINVIEW_PT2254_STROBE;
- gpio_write(data);
-}
-
-/* ----------------------------------------------------------------------- */
-/* mono/stereo control for various cards (which don't use i2c chips but */
-/* connect something to the GPIO pins */
-
-static void
-gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned int con = 0;
-
- if (set) {
- gpio_inout(0x300, 0x300);
- if (v->mode & VIDEO_SOUND_LANG1)
- con = 0x000;
- if (v->mode & VIDEO_SOUND_LANG2)
- con = 0x300;
- if (v->mode & VIDEO_SOUND_STEREO)
- con = 0x200;
-/* if (v->mode & VIDEO_SOUND_MONO)
- * con = 0x100; */
- gpio_bits(0x300, con);
- } else {
- v->mode = VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
-static void
-gvbctv5pci_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned int val, con;
-
- if (btv->radio_user)
- return;
-
- val = gpio_read();
- if (set) {
- con = 0x000;
- if (v->mode & VIDEO_SOUND_LANG2) {
- if (v->mode & VIDEO_SOUND_LANG1) {
- /* LANG1 + LANG2 */
- con = 0x100;
- }
- else {
- /* LANG2 */
- con = 0x300;
- }
- }
- if (con != (val & 0x300)) {
- gpio_bits(0x300, con);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"gvbctv5pci");
- }
- } else {
- switch (val & 0x70) {
- case 0x10:
- v->mode = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- break;
- case 0x30:
- v->mode = VIDEO_SOUND_LANG2;
- break;
- case 0x50:
- v->mode = VIDEO_SOUND_LANG1;
- break;
- case 0x60:
- v->mode = VIDEO_SOUND_STEREO;
- break;
- case 0x70:
- v->mode = VIDEO_SOUND_MONO;
- break;
- default:
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
- }
-}
-
-/*
- * Mario Medina Nussbaum <medisoft@alohabbs.org.mx>
- * I discover that on BT848_GPIO_DATA address a byte 0xcce enable stereo,
- * 0xdde enables mono and 0xccd enables sap
- *
- * Petr Vandrovec <VANDROVE@vc.cvut.cz>
- * P.S.: At least mask in line above is wrong - GPIO pins 3,2 select
- * input/output sound connection, so both must be set for output mode.
- *
- * Looks like it's needed only for the "tvphone", the "tvphone 98"
- * handles this with a tda9840
- *
- */
-static void
-avermedia_tvphone_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- int val = 0;
-
- if (set) {
- if (v->mode & VIDEO_SOUND_LANG2) /* SAP */
- val = 0x02;
- if (v->mode & VIDEO_SOUND_STEREO)
- val = 0x01;
- if (val) {
- gpio_bits(0x03,val);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"avermedia");
- }
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1;
- return;
- }
-}
-
-static void
-avermedia_tv_stereo_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- int val = 0;
-
- if (set) {
- if (v->mode & VIDEO_SOUND_LANG2) /* SAP */
- val = 0x01;
- if (v->mode & VIDEO_SOUND_STEREO) /* STEREO */
- val = 0x02;
- btaor(val, ~0x03, BT848_GPIO_DATA);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"avermedia");
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- return;
- }
-}
-
-/* Lifetec 9415 handling */
-static void
-lt9415_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- int val = 0;
-
- if (gpio_read() & 0x4000) {
- v->mode = VIDEO_SOUND_MONO;
- return;
- }
-
- if (set) {
- if (v->mode & VIDEO_SOUND_LANG2) /* A2 SAP */
- val = 0x0080;
- if (v->mode & VIDEO_SOUND_STEREO) /* A2 stereo */
- val = 0x0880;
- if ((v->mode & VIDEO_SOUND_LANG1) ||
- (v->mode & VIDEO_SOUND_MONO))
- val = 0;
- gpio_bits(0x0880, val);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"lt9415");
- } else {
- /* autodetect doesn't work with this card :-( */
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- return;
- }
-}
-
-/* TDA9821 on TerraTV+ Bt848, Bt878 */
-static void
-terratv_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned int con = 0;
-
- if (set) {
- gpio_inout(0x180000,0x180000);
- if (v->mode & VIDEO_SOUND_LANG2)
- con = 0x080000;
- if (v->mode & VIDEO_SOUND_STEREO)
- con = 0x180000;
- gpio_bits(0x180000, con);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"terratv");
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
-static void
-winfast2000_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned long val = 0;
-
- if (set) {
- /*btor (0xc32000, BT848_GPIO_OUT_EN);*/
- if (v->mode & VIDEO_SOUND_MONO) /* Mono */
- val = 0x420000;
- if (v->mode & VIDEO_SOUND_LANG1) /* Mono */
- val = 0x420000;
- if (v->mode & VIDEO_SOUND_LANG2) /* SAP */
- val = 0x410000;
- if (v->mode & VIDEO_SOUND_STEREO) /* Stereo */
- val = 0x020000;
- if (val) {
- gpio_bits(0x430000, val);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"winfast2000");
- }
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
-/*
- * Dariusz Kowalewski <darekk@automex.pl>
- * sound control for Prolink PV-BT878P+9B (PixelView PlayTV Pro FM+NICAM
- * revision 9B has on-board TDA9874A sound decoder).
- *
- * Note: There are card variants without tda9874a. Forcing the "stereo sound route"
- * will mute this cards.
- */
-static void
-pvbt878p9b_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned int val = 0;
-
- if (btv->radio_user)
- return;
-
- if (set) {
- if (v->mode & VIDEO_SOUND_MONO) {
- val = 0x01;
- }
- if ((v->mode & (VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2))
- || (v->mode & VIDEO_SOUND_STEREO)) {
- val = 0x02;
- }
- if (val) {
- gpio_bits(0x03,val);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"pvbt878p9b");
- }
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
-/*
- * Dariusz Kowalewski <darekk@automex.pl>
- * sound control for FlyVideo 2000S (with tda9874 decoder)
- * based on pvbt878p9b_audio() - this is not tested, please fix!!!
- */
-static void
-fv2000s_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned int val = 0xffff;
-
- if (btv->radio_user)
- return;
- if (set) {
- if (v->mode & VIDEO_SOUND_MONO) {
- val = 0x0000;
- }
- if ((v->mode & (VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2))
- || (v->mode & VIDEO_SOUND_STEREO)) {
- val = 0x1080; /*-dk-???: 0x0880, 0x0080, 0x1800 ... */
- }
- if (val != 0xffff) {
- gpio_bits(0x1800, val);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"fv2000s");
- }
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
-/*
- * sound control for Canopus WinDVR PCI
- * Masaki Suzuki <masaki@btree.org>
- */
-static void
-windvr_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned long val = 0;
-
- if (set) {
- if (v->mode & VIDEO_SOUND_MONO)
- val = 0x040000;
- if (v->mode & VIDEO_SOUND_LANG1)
- val = 0;
- if (v->mode & VIDEO_SOUND_LANG2)
- val = 0x100000;
- if (v->mode & VIDEO_SOUND_STEREO)
- val = 0;
- if (val) {
- gpio_bits(0x140000, val);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"windvr");
- }
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
-/*
- * sound control for AD-TVK503
- * Hiroshi Takekawa <sian@big.or.jp>
- */
-static void
-adtvk503_audio(struct bttv *btv, struct video_audio *v, int set)
-{
- unsigned int con = 0xffffff;
-
- /* btaor(0x1e0000, ~0x1e0000, BT848_GPIO_OUT_EN); */
-
- if (set) {
- /* btor(***, BT848_GPIO_OUT_EN); */
- if (v->mode & VIDEO_SOUND_LANG1)
- con = 0x00000000;
- if (v->mode & VIDEO_SOUND_LANG2)
- con = 0x00180000;
- if (v->mode & VIDEO_SOUND_STEREO)
- con = 0x00000000;
- if (v->mode & VIDEO_SOUND_MONO)
- con = 0x00060000;
- if (con != 0xffffff) {
- gpio_bits(0x1e0000,con);
- if (bttv_gpio)
- bttv_gpio_tracking(btv, "adtvk503");
- }
- } else {
- v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
- VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- }
-}
-
/* RemoteVision MX (rv605) muxsel helper [Miguel Freitas]
*
* This is needed because rv605 don't use a normal multiplex, but a crosspoint
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 581a3c955739..907dc62c1783 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -9,6 +9,12 @@
some v4l2 code lines are taken from Justin's bttv2 driver which is
(c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za>
+ V4L1 removal from:
+ (c) 2005-2006 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
+
+ Fixes to be fully V4L2 compliant by
+ (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+
Cropping and overscan support
Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at>
Sponsored by OPQ Systems AB
@@ -157,7 +163,7 @@ MODULE_LICENSE("GPL");
static ssize_t show_card(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vfd = to_video_device(cd);
+ struct video_device *vfd = container_of(cd, struct video_device, class_dev);
struct bttv *btv = dev_get_drvdata(vfd->dev);
return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
}
@@ -470,31 +476,27 @@ static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
/* ----------------------------------------------------------------------- */
/* bttv format list
packed pixel formats must come first */
-static const struct bttv_format bttv_formats[] = {
+static const struct bttv_format formats[] = {
{
.name = "8 bpp, gray",
- .palette = VIDEO_PALETTE_GREY,
.fourcc = V4L2_PIX_FMT_GREY,
.btformat = BT848_COLOR_FMT_Y8,
.depth = 8,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "8 bpp, dithered color",
- .palette = VIDEO_PALETTE_HI240,
.fourcc = V4L2_PIX_FMT_HI240,
.btformat = BT848_COLOR_FMT_RGB8,
.depth = 8,
.flags = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
},{
.name = "15 bpp RGB, le",
- .palette = VIDEO_PALETTE_RGB555,
.fourcc = V4L2_PIX_FMT_RGB555,
.btformat = BT848_COLOR_FMT_RGB15,
.depth = 16,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "15 bpp RGB, be",
- .palette = -1,
.fourcc = V4L2_PIX_FMT_RGB555X,
.btformat = BT848_COLOR_FMT_RGB15,
.btswap = 0x03, /* byteswap */
@@ -502,14 +504,12 @@ static const struct bttv_format bttv_formats[] = {
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "16 bpp RGB, le",
- .palette = VIDEO_PALETTE_RGB565,
.fourcc = V4L2_PIX_FMT_RGB565,
.btformat = BT848_COLOR_FMT_RGB16,
.depth = 16,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "16 bpp RGB, be",
- .palette = -1,
.fourcc = V4L2_PIX_FMT_RGB565X,
.btformat = BT848_COLOR_FMT_RGB16,
.btswap = 0x03, /* byteswap */
@@ -517,21 +517,18 @@ static const struct bttv_format bttv_formats[] = {
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "24 bpp RGB, le",
- .palette = VIDEO_PALETTE_RGB24,
.fourcc = V4L2_PIX_FMT_BGR24,
.btformat = BT848_COLOR_FMT_RGB24,
.depth = 24,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "32 bpp RGB, le",
- .palette = VIDEO_PALETTE_RGB32,
.fourcc = V4L2_PIX_FMT_BGR32,
.btformat = BT848_COLOR_FMT_RGB32,
.depth = 32,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "32 bpp RGB, be",
- .palette = -1,
.fourcc = V4L2_PIX_FMT_RGB32,
.btformat = BT848_COLOR_FMT_RGB32,
.btswap = 0x0f, /* byte+word swap */
@@ -539,21 +536,18 @@ static const struct bttv_format bttv_formats[] = {
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "4:2:2, packed, YUYV",
- .palette = VIDEO_PALETTE_YUV422,
.fourcc = V4L2_PIX_FMT_YUYV,
.btformat = BT848_COLOR_FMT_YUY2,
.depth = 16,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "4:2:2, packed, YUYV",
- .palette = VIDEO_PALETTE_YUYV,
.fourcc = V4L2_PIX_FMT_YUYV,
.btformat = BT848_COLOR_FMT_YUY2,
.depth = 16,
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "4:2:2, packed, UYVY",
- .palette = VIDEO_PALETTE_UYVY,
.fourcc = V4L2_PIX_FMT_UYVY,
.btformat = BT848_COLOR_FMT_YUY2,
.btswap = 0x03, /* byteswap */
@@ -561,7 +555,6 @@ static const struct bttv_format bttv_formats[] = {
.flags = FORMAT_FLAGS_PACKED,
},{
.name = "4:2:2, planar, Y-Cb-Cr",
- .palette = VIDEO_PALETTE_YUV422P,
.fourcc = V4L2_PIX_FMT_YUV422P,
.btformat = BT848_COLOR_FMT_YCrCb422,
.depth = 16,
@@ -570,7 +563,6 @@ static const struct bttv_format bttv_formats[] = {
.vshift = 0,
},{
.name = "4:2:0, planar, Y-Cb-Cr",
- .palette = VIDEO_PALETTE_YUV420P,
.fourcc = V4L2_PIX_FMT_YUV420,
.btformat = BT848_COLOR_FMT_YCrCb422,
.depth = 12,
@@ -579,7 +571,6 @@ static const struct bttv_format bttv_formats[] = {
.vshift = 1,
},{
.name = "4:2:0, planar, Y-Cr-Cb",
- .palette = -1,
.fourcc = V4L2_PIX_FMT_YVU420,
.btformat = BT848_COLOR_FMT_YCrCb422,
.depth = 12,
@@ -588,7 +579,6 @@ static const struct bttv_format bttv_formats[] = {
.vshift = 1,
},{
.name = "4:1:1, planar, Y-Cb-Cr",
- .palette = VIDEO_PALETTE_YUV411P,
.fourcc = V4L2_PIX_FMT_YUV411P,
.btformat = BT848_COLOR_FMT_YCrCb411,
.depth = 12,
@@ -597,7 +587,6 @@ static const struct bttv_format bttv_formats[] = {
.vshift = 0,
},{
.name = "4:1:0, planar, Y-Cb-Cr",
- .palette = VIDEO_PALETTE_YUV410P,
.fourcc = V4L2_PIX_FMT_YUV410,
.btformat = BT848_COLOR_FMT_YCrCb411,
.depth = 9,
@@ -606,7 +595,6 @@ static const struct bttv_format bttv_formats[] = {
.vshift = 2,
},{
.name = "4:1:0, planar, Y-Cr-Cb",
- .palette = -1,
.fourcc = V4L2_PIX_FMT_YVU410,
.btformat = BT848_COLOR_FMT_YCrCb411,
.depth = 9,
@@ -615,14 +603,13 @@ static const struct bttv_format bttv_formats[] = {
.vshift = 2,
},{
.name = "raw scanlines",
- .palette = VIDEO_PALETTE_RAW,
.fourcc = -1,
.btformat = BT848_COLOR_FMT_RAW,
.depth = 8,
.flags = FORMAT_FLAGS_RAW,
}
};
-static const unsigned int BTTV_FORMATS = ARRAY_SIZE(bttv_formats);
+static const unsigned int FORMATS = ARRAY_SIZE(formats);
/* ----------------------------------------------------------------------- */
@@ -798,7 +785,17 @@ static const struct v4l2_queryctrl bttv_ctls[] = {
};
-static const int BTTV_CTLS = ARRAY_SIZE(bttv_ctls);
+
+static const struct v4l2_queryctrl *ctrl_by_id(int id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bttv_ctls); i++)
+ if (bttv_ctls[i].id == id)
+ return bttv_ctls+i;
+
+ return NULL;
+}
/* ----------------------------------------------------------------------- */
/* resource management */
@@ -1255,16 +1252,6 @@ audio_input(struct bttv *btv, int input)
}
static void
-i2c_vidiocschan(struct bttv *btv)
-{
- v4l2_std_id std = bttv_tvnorms[btv->tvnorm].v4l2_id;
-
- bttv_call_i2c_clients(btv, VIDIOC_S_STD, &std);
- if (btv->c.type == BTTV_BOARD_VOODOOTV_FM || btv->c.type == BTTV_BOARD_VOODOOTV_200)
- bttv_tda9880_setnorm(btv,btv->tvnorm);
-}
-
-static void
bttv_crop_calc_limits(struct bttv_crop *c)
{
/* Scale factor min. 1:1, max. 16:1. Min. image size
@@ -1298,6 +1285,7 @@ static int
set_tvnorm(struct bttv *btv, unsigned int norm)
{
const struct bttv_tvnorm *tvnorm;
+ v4l2_std_id id;
if (norm < 0 || norm >= BTTV_TVNORMS)
return -EINVAL;
@@ -1334,6 +1322,9 @@ set_tvnorm(struct bttv *btv, unsigned int norm)
bttv_tda9880_setnorm(btv,norm);
break;
}
+ id = tvnorm->v4l2_id;
+ bttv_call_i2c_clients(btv, VIDIOC_S_STD, &id);
+
return 0;
}
@@ -1359,7 +1350,6 @@ set_input(struct bttv *btv, unsigned int input, unsigned int norm)
audio_input(btv,(input == bttv_tvcards[btv->c.type].tuner ?
TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN));
set_tvnorm(btv, norm);
- i2c_vidiocschan(btv);
}
static void init_irqreg(struct bttv *btv)
@@ -1452,38 +1442,12 @@ static void bttv_reinit_bt848(struct bttv *btv)
set_input(btv, btv->input, btv->tvnorm);
}
-static int get_control(struct bttv *btv, struct v4l2_control *c)
+static int bttv_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
{
- struct video_audio va;
- int i;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- for (i = 0; i < BTTV_CTLS; i++)
- if (bttv_ctls[i].id == c->id)
- break;
- if (i == BTTV_CTLS)
- return -EINVAL;
- if (btv->audio_hook && i >= 4 && i <= 8) {
- memset(&va,0,sizeof(va));
- btv->audio_hook(btv,&va,0);
- switch (c->id) {
- case V4L2_CID_AUDIO_MUTE:
- c->value = (VIDEO_AUDIO_MUTE & va.flags) ? 1 : 0;
- break;
- case V4L2_CID_AUDIO_VOLUME:
- c->value = va.volume;
- break;
- case V4L2_CID_AUDIO_BALANCE:
- c->value = va.balance;
- break;
- case V4L2_CID_AUDIO_BASS:
- c->value = va.bass;
- break;
- case V4L2_CID_AUDIO_TREBLE:
- c->value = va.treble;
- break;
- }
- return 0;
- }
switch (c->id) {
case V4L2_CID_BRIGHTNESS:
c->value = btv->bright;
@@ -1503,7 +1467,7 @@ static int get_control(struct bttv *btv, struct v4l2_control *c)
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
- bttv_call_i2c_clients(btv,VIDIOC_G_CTRL,c);
+ bttv_call_i2c_clients(btv, VIDIOC_G_CTRL, c);
break;
case V4L2_CID_PRIVATE_CHROMA_AGC:
@@ -1545,67 +1509,44 @@ static int get_control(struct bttv *btv, struct v4l2_control *c)
return 0;
}
-static int set_control(struct bttv *btv, struct v4l2_control *c)
+static int bttv_s_ctrl(struct file *file, void *f,
+ struct v4l2_control *c)
{
- struct video_audio va;
- int i,val;
+ int err;
+ int val;
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- for (i = 0; i < BTTV_CTLS; i++)
- if (bttv_ctls[i].id == c->id)
- break;
- if (i == BTTV_CTLS)
- return -EINVAL;
- if (btv->audio_hook && i >= 4 && i <= 8) {
- memset(&va,0,sizeof(va));
- btv->audio_hook(btv,&va,0);
- switch (c->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (c->value) {
- va.flags |= VIDEO_AUDIO_MUTE;
- audio_mute(btv, 1);
- } else {
- va.flags &= ~VIDEO_AUDIO_MUTE;
- audio_mute(btv, 0);
- }
- break;
+ err = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != err)
+ return err;
- case V4L2_CID_AUDIO_VOLUME:
- va.volume = c->value;
- break;
- case V4L2_CID_AUDIO_BALANCE:
- va.balance = c->value;
- break;
- case V4L2_CID_AUDIO_BASS:
- va.bass = c->value;
- break;
- case V4L2_CID_AUDIO_TREBLE:
- va.treble = c->value;
- break;
- }
- btv->audio_hook(btv,&va,1);
- return 0;
- }
switch (c->id) {
case V4L2_CID_BRIGHTNESS:
- bt848_bright(btv,c->value);
+ bt848_bright(btv, c->value);
break;
case V4L2_CID_HUE:
- bt848_hue(btv,c->value);
+ bt848_hue(btv, c->value);
break;
case V4L2_CID_CONTRAST:
- bt848_contrast(btv,c->value);
+ bt848_contrast(btv, c->value);
break;
case V4L2_CID_SATURATION:
- bt848_sat(btv,c->value);
+ bt848_sat(btv, c->value);
break;
case V4L2_CID_AUDIO_MUTE:
audio_mute(btv, c->value);
/* fall through */
case V4L2_CID_AUDIO_VOLUME:
+ if (btv->volume_gpio)
+ btv->volume_gpio(btv, c->value);
+
+ bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, c);
+ break;
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
- bttv_call_i2c_clients(btv,VIDIOC_S_CTRL,c);
+ bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, c);
break;
case V4L2_CID_PRIVATE_CHROMA_AGC:
@@ -1632,8 +1573,9 @@ static int set_control(struct bttv *btv, struct v4l2_control *c)
break;
case V4L2_CID_PRIVATE_AGC_CRUSH:
btv->opt_adc_crush = c->value;
- btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
- BT848_ADC);
+ btwrite(BT848_ADC_RESERVED |
+ (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+ BT848_ADC);
break;
case V4L2_CID_PRIVATE_VCR_HACK:
btv->opt_vcr_hack = c->value;
@@ -1693,29 +1635,15 @@ static void bttv_field_count(struct bttv *btv)
}
static const struct bttv_format*
-format_by_palette(int palette)
-{
- unsigned int i;
-
- for (i = 0; i < BTTV_FORMATS; i++) {
- if (-1 == bttv_formats[i].palette)
- continue;
- if (bttv_formats[i].palette == palette)
- return bttv_formats+i;
- }
- return NULL;
-}
-
-static const struct bttv_format*
format_by_fourcc(int fourcc)
{
unsigned int i;
- for (i = 0; i < BTTV_FORMATS; i++) {
- if (-1 == bttv_formats[i].fourcc)
+ for (i = 0; i < FORMATS; i++) {
+ if (-1 == formats[i].fourcc)
continue;
- if (bttv_formats[i].fourcc == fourcc)
- return bttv_formats+i;
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
}
return NULL;
}
@@ -1733,7 +1661,7 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
dprintk("switch_overlay: enter [new=%p]\n",new);
if (new)
- new->vb.state = STATE_DONE;
+ new->vb.state = VIDEOBUF_DONE;
spin_lock_irqsave(&btv->s_lock,flags);
old = btv->screen;
btv->screen = new;
@@ -1844,7 +1772,7 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
}
/* alloc risc memory */
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
redo_dma_risc = 1;
if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf)))
goto fail;
@@ -1854,7 +1782,7 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
if (0 != (rc = bttv_buffer_risc(btv,buf)))
goto fail;
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
@@ -1893,7 +1821,7 @@ buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct bttv_fh *fh = q->priv_data;
struct bttv *btv = fh->btv;
- buf->vb.state = STATE_QUEUED;
+ buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue,&btv->capture);
if (!btv->curr.frame_irq) {
btv->loop_irq |= 1;
@@ -1916,374 +1844,234 @@ static struct videobuf_queue_ops bttv_video_qops = {
.buf_release = buffer_release,
};
-static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
+static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id)
{
- switch (cmd) {
- case BTTV_VERSION:
- return BTTV_VERSION_CODE;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ unsigned int i;
+ int err;
- /* *** v4l1 *** ************************************************ */
- case VIDIOCGFREQ:
- {
- unsigned long *freq = arg;
- *freq = btv->freq;
- return 0;
- }
- case VIDIOCSFREQ:
- {
- struct v4l2_frequency freq;
+ err = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != err)
+ return err;
- memset(&freq, 0, sizeof(freq));
- freq.frequency = *(unsigned long *)arg;
- mutex_lock(&btv->lock);
- freq.type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- btv->freq = *(unsigned long *)arg;
- bttv_call_i2c_clients(btv,VIDIOC_S_FREQUENCY,&freq);
- if (btv->has_matchbox && btv->radio_user)
- tea5757_set_freq(btv,*(unsigned long *)arg);
- mutex_unlock(&btv->lock);
- return 0;
- }
+ for (i = 0; i < BTTV_TVNORMS; i++)
+ if (*id & bttv_tvnorms[i].v4l2_id)
+ break;
+ if (i == BTTV_TVNORMS)
+ return -EINVAL;
- case VIDIOCGTUNER:
- {
- struct video_tuner *v = arg;
+ mutex_lock(&btv->lock);
+ set_tvnorm(btv, i);
+ mutex_unlock(&btv->lock);
- if (UNSET == bttv_tvcards[btv->c.type].tuner)
- return -EINVAL;
- if (v->tuner) /* Only tuner 0 */
- return -EINVAL;
- strcpy(v->name, "Television");
- v->rangelow = 0;
- v->rangehigh = 0x7FFFFFFF;
- v->flags = VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
- v->mode = btv->tvnorm;
- v->signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0;
- bttv_call_i2c_clients(btv,cmd,v);
- return 0;
- }
- case VIDIOCSTUNER:
- {
- struct video_tuner *v = arg;
+ return 0;
+}
- if (v->tuner) /* Only tuner 0 */
- return -EINVAL;
- if (v->mode >= BTTV_TVNORMS)
- return -EINVAL;
+static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- mutex_lock(&btv->lock);
- set_tvnorm(btv,v->mode);
- bttv_call_i2c_clients(btv,cmd,v);
- mutex_unlock(&btv->lock);
- return 0;
- }
+ if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
+ *id = V4L2_STD_625_50;
+ else
+ *id = V4L2_STD_525_60;
+ return 0;
+}
- case VIDIOCGCHAN:
- {
- struct video_channel *v = arg;
- unsigned int channel = v->channel;
+static int bttv_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ unsigned int n;
- if (channel >= bttv_tvcards[btv->c.type].video_inputs)
- return -EINVAL;
- v->tuners=0;
- v->flags = VIDEO_VC_AUDIO;
- v->type = VIDEO_TYPE_CAMERA;
- v->norm = btv->tvnorm;
- if (channel == bttv_tvcards[btv->c.type].tuner) {
- strcpy(v->name,"Television");
- v->flags|=VIDEO_VC_TUNER;
- v->type=VIDEO_TYPE_TV;
- v->tuners=1;
- } else if (channel == btv->svhs) {
- strcpy(v->name,"S-Video");
- } else {
- sprintf(v->name,"Composite%d",channel);
- }
- return 0;
- }
- case VIDIOCSCHAN:
- {
- struct video_channel *v = arg;
- unsigned int channel = v->channel;
+ n = i->index;
- if (channel >= bttv_tvcards[btv->c.type].video_inputs)
- return -EINVAL;
- if (v->norm >= BTTV_TVNORMS)
- return -EINVAL;
+ if (n >= bttv_tvcards[btv->c.type].video_inputs)
+ return -EINVAL;
- mutex_lock(&btv->lock);
- if (channel == btv->input &&
- v->norm == btv->tvnorm) {
- /* nothing to do */
- mutex_unlock(&btv->lock);
- return 0;
- }
+ memset(i, 0, sizeof(*i));
- set_input(btv, v->channel, v->norm);
- mutex_unlock(&btv->lock);
- return 0;
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->audioset = 1;
+
+ if (i->index == bttv_tvcards[btv->c.type].tuner) {
+ sprintf(i->name, "Television");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->tuner = 0;
+ } else if (i->index == btv->svhs) {
+ sprintf(i->name, "S-Video");
+ } else {
+ sprintf(i->name, "Composite%d", i->index);
}
- case VIDIOCGAUDIO:
- {
- struct video_audio *v = arg;
+ if (i->index == btv->input) {
+ __u32 dstatus = btread(BT848_DSTATUS);
+ if (0 == (dstatus & BT848_DSTATUS_PRES))
+ i->status |= V4L2_IN_ST_NO_SIGNAL;
+ if (0 == (dstatus & BT848_DSTATUS_HLOC))
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ }
- memset(v,0,sizeof(*v));
- strcpy(v->name,"Television");
- v->flags |= VIDEO_AUDIO_MUTABLE;
- v->mode = VIDEO_SOUND_MONO;
+ for (n = 0; n < BTTV_TVNORMS; n++)
+ i->std |= bttv_tvnorms[n].v4l2_id;
- mutex_lock(&btv->lock);
- bttv_call_i2c_clients(btv,cmd,v);
+ return 0;
+}
- /* card specific hooks */
- if (btv->audio_hook)
- btv->audio_hook(btv,v,0);
+static int bttv_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- mutex_unlock(&btv->lock);
- return 0;
- }
- case VIDIOCSAUDIO:
- {
- struct video_audio *v = arg;
- unsigned int audio = v->audio;
+ *i = btv->input;
+ return 0;
+}
- if (audio >= bttv_tvcards[btv->c.type].audio_inputs)
- return -EINVAL;
+static int bttv_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- mutex_lock(&btv->lock);
- audio_mute(btv, (v->flags&VIDEO_AUDIO_MUTE) ? 1 : 0);
- bttv_call_i2c_clients(btv,cmd,v);
+ int err;
- /* card specific hooks */
- if (btv->audio_hook)
- btv->audio_hook(btv,v,1);
+ err = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != err)
+ return err;
- mutex_unlock(&btv->lock);
- return 0;
- }
+ if (i > bttv_tvcards[btv->c.type].video_inputs)
+ return -EINVAL;
- /* *** v4l2 *** ************************************************ */
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *e = arg;
- unsigned int index = e->index;
+ mutex_lock(&btv->lock);
+ set_input(btv, i, btv->tvnorm);
+ mutex_unlock(&btv->lock);
+ return 0;
+}
- if (index >= BTTV_TVNORMS)
- return -EINVAL;
- v4l2_video_std_construct(e, bttv_tvnorms[e->index].v4l2_id,
- bttv_tvnorms[e->index].name);
- e->index = index;
- return 0;
- }
- case VIDIOC_G_STD:
- {
- v4l2_std_id *id = arg;
- *id = bttv_tvnorms[btv->tvnorm].v4l2_id;
- return 0;
- }
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg;
- unsigned int i;
+static int bttv_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int err;
- for (i = 0; i < BTTV_TVNORMS; i++)
- if (*id & bttv_tvnorms[i].v4l2_id)
- break;
- if (i == BTTV_TVNORMS)
- return -EINVAL;
+ err = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != err)
+ return err;
- mutex_lock(&btv->lock);
- set_tvnorm(btv,i);
- i2c_vidiocschan(btv);
- mutex_unlock(&btv->lock);
- return 0;
- }
- case VIDIOC_QUERYSTD:
- {
- v4l2_std_id *id = arg;
+ if (UNSET == bttv_tvcards[btv->c.type].tuner)
+ return -EINVAL;
- if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
- *id = V4L2_STD_625_50;
- else
- *id = V4L2_STD_525_60;
- return 0;
- }
+ if (0 != t->index)
+ return -EINVAL;
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *i = arg;
- unsigned int n;
+ mutex_lock(&btv->lock);
+ bttv_call_i2c_clients(btv, VIDIOC_S_TUNER, t);
- n = i->index;
- if (n >= bttv_tvcards[btv->c.type].video_inputs)
- return -EINVAL;
- memset(i,0,sizeof(*i));
- i->index = n;
- i->type = V4L2_INPUT_TYPE_CAMERA;
- i->audioset = 1;
- if (i->index == bttv_tvcards[btv->c.type].tuner) {
- sprintf(i->name, "Television");
- i->type = V4L2_INPUT_TYPE_TUNER;
- i->tuner = 0;
- } else if (i->index == btv->svhs) {
- sprintf(i->name, "S-Video");
- } else {
- sprintf(i->name,"Composite%d",i->index);
- }
- if (i->index == btv->input) {
- __u32 dstatus = btread(BT848_DSTATUS);
- if (0 == (dstatus & BT848_DSTATUS_PRES))
- i->status |= V4L2_IN_ST_NO_SIGNAL;
- if (0 == (dstatus & BT848_DSTATUS_HLOC))
- i->status |= V4L2_IN_ST_NO_H_LOCK;
- }
- for (n = 0; n < BTTV_TVNORMS; n++)
- i->std |= bttv_tvnorms[n].v4l2_id;
- return 0;
- }
- case VIDIOC_G_INPUT:
- {
- int *i = arg;
- *i = btv->input;
- return 0;
- }
- case VIDIOC_S_INPUT:
- {
- unsigned int *i = arg;
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 1);
- if (*i > bttv_tvcards[btv->c.type].video_inputs)
- return -EINVAL;
- mutex_lock(&btv->lock);
- set_input(btv, *i, btv->tvnorm);
- mutex_unlock(&btv->lock);
- return 0;
- }
+ mutex_unlock(&btv->lock);
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *t = arg;
+ return 0;
+}
- if (UNSET == bttv_tvcards[btv->c.type].tuner)
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
- mutex_lock(&btv->lock);
- memset(t,0,sizeof(*t));
- t->rxsubchans = V4L2_TUNER_SUB_MONO;
- bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t);
- strcpy(t->name, "Television");
- t->capability = V4L2_TUNER_CAP_NORM;
- t->type = V4L2_TUNER_ANALOG_TV;
- if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
- t->signal = 0xffff;
-
- if (btv->audio_hook) {
- /* Hmmm ... */
- struct video_audio va;
- memset(&va, 0, sizeof(struct video_audio));
- btv->audio_hook(btv,&va,0);
- t->audmode = V4L2_TUNER_MODE_MONO;
- t->rxsubchans = V4L2_TUNER_SUB_MONO;
- if(va.mode & VIDEO_SOUND_STEREO) {
- t->audmode = V4L2_TUNER_MODE_STEREO;
- t->rxsubchans = V4L2_TUNER_SUB_STEREO;
- }
- if(va.mode & VIDEO_SOUND_LANG2) {
- t->audmode = V4L2_TUNER_MODE_LANG1;
- t->rxsubchans = V4L2_TUNER_SUB_LANG1
- | V4L2_TUNER_SUB_LANG2;
- }
- }
- /* FIXME: fill capability+audmode */
- mutex_unlock(&btv->lock);
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *t = arg;
+static int bttv_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int err;
- if (UNSET == bttv_tvcards[btv->c.type].tuner)
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
- mutex_lock(&btv->lock);
- bttv_call_i2c_clients(btv, VIDIOC_S_TUNER, t);
- if (btv->audio_hook) {
- struct video_audio va;
- memset(&va, 0, sizeof(struct video_audio));
- if (t->audmode == V4L2_TUNER_MODE_MONO)
- va.mode = VIDEO_SOUND_MONO;
- else if (t->audmode == V4L2_TUNER_MODE_STEREO ||
- t->audmode == V4L2_TUNER_MODE_LANG1_LANG2)
- va.mode = VIDEO_SOUND_STEREO;
- else if (t->audmode == V4L2_TUNER_MODE_LANG1)
- va.mode = VIDEO_SOUND_LANG1;
- else if (t->audmode == V4L2_TUNER_MODE_LANG2)
- va.mode = VIDEO_SOUND_LANG2;
- btv->audio_hook(btv,&va,1);
- }
- mutex_unlock(&btv->lock);
- return 0;
- }
+ err = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != err)
+ return err;
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ f->type = V4L2_TUNER_ANALOG_TV;
+ f->frequency = btv->freq;
- memset(f,0,sizeof(*f));
- f->type = V4L2_TUNER_ANALOG_TV;
- f->frequency = btv->freq;
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ return 0;
+}
+
+static int bttv_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int err;
+
+ err = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != err)
+ return err;
+
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+ if (unlikely(f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ mutex_lock(&btv->lock);
+ btv->freq = f->frequency;
+ bttv_call_i2c_clients(btv, VIDIOC_S_FREQUENCY, f);
+ if (btv->has_matchbox && btv->radio_user)
+ tea5757_set_freq(btv, btv->freq);
+ mutex_unlock(&btv->lock);
+ return 0;
+}
+
+static int bttv_log_status(struct file *file, void *f)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ printk(KERN_INFO "bttv%d: ======== START STATUS CARD #%d ========\n",
+ btv->c.nr, btv->c.nr);
+ bttv_call_i2c_clients(btv, VIDIOC_LOG_STATUS, NULL);
+ printk(KERN_INFO "bttv%d: ======== END STATUS CARD #%d ========\n",
+ btv->c.nr, btv->c.nr);
+ return 0;
+}
- if (unlikely(f->tuner != 0))
- return -EINVAL;
- if (unlikely (f->type != V4L2_TUNER_ANALOG_TV))
- return -EINVAL;
- mutex_lock(&btv->lock);
- btv->freq = f->frequency;
- bttv_call_i2c_clients(btv,VIDIOC_S_FREQUENCY,f);
- if (btv->has_matchbox && btv->radio_user)
- tea5757_set_freq(btv,btv->freq);
- mutex_unlock(&btv->lock);
- return 0;
- }
- case VIDIOC_LOG_STATUS:
- {
- printk(KERN_INFO "bttv%d: ================= START STATUS CARD #%d =================\n", btv->c.nr, btv->c.nr);
- bttv_call_i2c_clients(btv, VIDIOC_LOG_STATUS, NULL);
- printk(KERN_INFO "bttv%d: ================== END STATUS CARD #%d ==================\n", btv->c.nr, btv->c.nr);
- return 0;
- }
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
- return -EINVAL;
- /* bt848 has a 12-bit register space */
- reg->reg &= 0xfff;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = btread(reg->reg);
- else
- btwrite(reg->val, reg->reg);
- return 0;
- }
-#endif
+static int bttv_g_register(struct file *file, void *f,
+ struct v4l2_register *reg)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- default:
- return -ENOIOCTLCMD;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+
+ /* bt848 has a 12-bit register space */
+ reg->reg &= 0xfff;
+ reg->val = btread(reg->reg);
+
+ return 0;
+}
+
+static int bttv_s_register(struct file *file, void *f,
+ struct v4l2_register *reg)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+
+ /* bt848 has a 12-bit register space */
+ reg->reg &= 0xfff;
+ btwrite(reg->val, reg->reg);
- }
return 0;
}
+#endif
/* Given cropping boundaries b and the scaled width and height of a
single field or frame, which must not exceed hardware limits, this
@@ -2659,983 +2447,681 @@ pix_format_set_size (struct v4l2_pix_format * f,
}
}
-static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f)
+static int bttv_g_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- memset(&f->fmt.pix,0,sizeof(struct v4l2_pix_format));
- pix_format_set_size (&f->fmt.pix, fh->fmt,
- fh->width, fh->height);
- f->fmt.pix.field = fh->cap.field;
- f->fmt.pix.pixelformat = fh->fmt->fourcc;
- return 0;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- memset(&f->fmt.win,0,sizeof(struct v4l2_window));
- f->fmt.win.w = fh->ov.w;
- f->fmt.win.field = fh->ov.field;
- return 0;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- bttv_vbi_get_fmt(fh, &f->fmt.vbi);
- return 0;
- default:
- return -EINVAL;
- }
+ struct bttv_fh *fh = priv;
+
+ pix_format_set_size(&f->fmt.pix, fh->fmt,
+ fh->width, fh->height);
+ f->fmt.pix.field = fh->cap.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+
+ return 0;
}
-static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv,
- struct v4l2_format *f, int adjust_crop)
+static int bttv_g_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- {
- const struct bttv_format *fmt;
- enum v4l2_field field;
- __s32 width, height;
- int rc;
+ struct bttv_fh *fh = priv;
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
+ f->fmt.win.w = fh->ov.w;
+ f->fmt.win.field = fh->ov.field;
- field = f->fmt.pix.field;
- if (V4L2_FIELD_ANY == field) {
- __s32 height2;
+ return 0;
+}
- height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
- field = (f->fmt.pix.height > height2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- }
- if (V4L2_FIELD_SEQ_BT == field)
- field = V4L2_FIELD_SEQ_TB;
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- case V4L2_FIELD_ALTERNATE:
- case V4L2_FIELD_INTERLACED:
- break;
- case V4L2_FIELD_SEQ_TB:
- if (fmt->flags & FORMAT_FLAGS_PLANAR)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
+static int bttv_try_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ const struct bttv_format *fmt;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ enum v4l2_field field;
+ __s32 width, height;
+ int rc;
- width = f->fmt.pix.width;
- height = f->fmt.pix.height;
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
- rc = limit_scaled_size(fh, &width, &height, field,
- /* width_mask: 4 pixels */ ~3,
- /* width_bias: nearest */ 2,
- /* adjust_size */ 1,
- adjust_crop);
- if (0 != rc)
- return rc;
+ field = f->fmt.pix.field;
- /* update data for the application */
- f->fmt.pix.field = field;
- pix_format_set_size(&f->fmt.pix, fmt, width, height);
+ if (V4L2_FIELD_ANY == field) {
+ __s32 height2;
- return 0;
+ height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
+ field = (f->fmt.pix.height > height2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
}
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- return verify_window(fh, &f->fmt.win,
- /* adjust_size */ 1,
- /* adjust_crop */ 0);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- return bttv_vbi_try_fmt(fh, &f->fmt.vbi);
+
+ if (V4L2_FIELD_SEQ_BT == field)
+ field = V4L2_FIELD_SEQ_TB;
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ case V4L2_FIELD_INTERLACED:
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ if (fmt->flags & FORMAT_FLAGS_PLANAR)
+ return -EINVAL;
+ break;
default:
return -EINVAL;
}
+
+ width = f->fmt.pix.width;
+ height = f->fmt.pix.height;
+
+ rc = limit_scaled_size(fh, &width, &height, field,
+ /* width_mask: 4 pixels */ ~3,
+ /* width_bias: nearest */ 2,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 0);
+ if (0 != rc)
+ return rc;
+
+ /* update data for the application */
+ f->fmt.pix.field = field;
+ pix_format_set_size(&f->fmt.pix, fmt, width, height);
+
+ return 0;
}
-static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv,
- struct v4l2_format *f)
+static int bttv_try_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+
+ return verify_window(fh, &f->fmt.win,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 0);
+}
+
+static int bttv_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
int retval;
+ const struct bttv_format *fmt;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ __s32 width, height;
+ enum v4l2_field field;
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- {
- const struct bttv_format *fmt;
+ retval = bttv_switch_type(fh, f->type);
+ if (0 != retval)
+ return retval;
- retval = bttv_switch_type(fh,f->type);
- if (0 != retval)
- return retval;
- retval = bttv_try_fmt(fh,btv,f, /* adjust_crop */ 1);
- if (0 != retval)
- return retval;
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ retval = bttv_try_fmt_cap(file, priv, f);
+ if (0 != retval)
+ return retval;
- /* update our state informations */
- mutex_lock(&fh->cap.lock);
- fh->fmt = fmt;
- fh->cap.field = f->fmt.pix.field;
- fh->cap.last = V4L2_FIELD_NONE;
- fh->width = f->fmt.pix.width;
- fh->height = f->fmt.pix.height;
- btv->init.fmt = fmt;
- btv->init.width = f->fmt.pix.width;
- btv->init.height = f->fmt.pix.height;
- mutex_unlock(&fh->cap.lock);
+ width = f->fmt.pix.width;
+ height = f->fmt.pix.height;
+ field = f->fmt.pix.field;
- return 0;
- }
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (no_overlay > 0) {
- printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
- return setup_window(fh, btv, &f->fmt.win, 1);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- retval = bttv_switch_type(fh,f->type);
- if (0 != retval)
- return retval;
- return bttv_vbi_set_fmt(fh, &f->fmt.vbi);
- default:
- return -EINVAL;
- }
-}
+ retval = limit_scaled_size(fh, &width, &height, f->fmt.pix.field,
+ /* width_mask: 4 pixels */ ~3,
+ /* width_bias: nearest */ 2,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 1);
+ if (0 != retval)
+ return retval;
-static int bttv_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
-{
- struct bttv_fh *fh = file->private_data;
- struct bttv *btv = fh->btv;
- unsigned long flags;
- int retval = 0;
+ f->fmt.pix.field = field;
- if (bttv_debug > 1)
- v4l_print_ioctl(btv->c.name, cmd);
-
- if (btv->errors)
- bttv_reinit_bt848(btv);
-
- switch (cmd) {
- case VIDIOCSFREQ:
- case VIDIOCSTUNER:
- case VIDIOCSCHAN:
- case VIDIOC_S_CTRL:
- case VIDIOC_S_STD:
- case VIDIOC_S_INPUT:
- case VIDIOC_S_TUNER:
- case VIDIOC_S_FREQUENCY:
- retval = v4l2_prio_check(&btv->prio,&fh->prio);
- if (0 != retval)
- return retval;
- };
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- switch (cmd) {
+ /* update our state informations */
+ mutex_lock(&fh->cap.lock);
+ fh->fmt = fmt;
+ fh->cap.field = f->fmt.pix.field;
+ fh->cap.last = V4L2_FIELD_NONE;
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ btv->init.fmt = fmt;
+ btv->init.width = f->fmt.pix.width;
+ btv->init.height = f->fmt.pix.height;
+ mutex_unlock(&fh->cap.lock);
- /* *** v4l1 *** ************************************************ */
- case VIDIOCGCAP:
- {
- struct video_capability *cap = arg;
+ return 0;
+}
- memset(cap,0,sizeof(*cap));
- strcpy(cap->name,btv->video_dev->name);
- if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
- /* vbi */
- cap->type = VID_TYPE_TUNER|VID_TYPE_TELETEXT;
- } else {
- /* others */
- cap->type = VID_TYPE_CAPTURE|
- VID_TYPE_TUNER|
- VID_TYPE_CLIPPING|
- VID_TYPE_SCALES;
- if (no_overlay <= 0)
- cap->type |= VID_TYPE_OVERLAY;
-
- cap->maxwidth = bttv_tvnorms[btv->tvnorm].swidth;
- cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight;
- cap->minwidth = 48;
- cap->minheight = 32;
- }
- cap->channels = bttv_tvcards[btv->c.type].video_inputs;
- cap->audios = bttv_tvcards[btv->c.type].audio_inputs;
- return 0;
- }
+static int bttv_s_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- case VIDIOCGPICT:
- {
- struct video_picture *pic = arg;
-
- memset(pic,0,sizeof(*pic));
- pic->brightness = btv->bright;
- pic->contrast = btv->contrast;
- pic->hue = btv->hue;
- pic->colour = btv->saturation;
- if (fh->fmt) {
- pic->depth = fh->fmt->depth;
- pic->palette = fh->fmt->palette;
- }
- return 0;
+ if (no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
}
- case VIDIOCSPICT:
- {
- struct video_picture *pic = arg;
- const struct bttv_format *fmt;
- fmt = format_by_palette(pic->palette);
- if (NULL == fmt)
- return -EINVAL;
- mutex_lock(&fh->cap.lock);
- if (fmt->flags & FORMAT_FLAGS_RAW) {
- /* VIDIOCMCAPTURE uses gbufsize, not RAW_BPL *
- RAW_LINES * 2. F1 is stored at offset 0, F2
- at buffer size / 2. */
- fh->width = RAW_BPL;
- fh->height = gbufsize / RAW_BPL;
- btv->init.width = RAW_BPL;
- btv->init.height = gbufsize / RAW_BPL;
- }
- fh->ovfmt = fmt;
- fh->fmt = fmt;
- btv->init.ovfmt = fmt;
- btv->init.fmt = fmt;
- if (bigendian) {
- /* dirty hack time: swap bytes for overlay if the
- display adaptor is big endian (insmod option) */
- if (fmt->palette == VIDEO_PALETTE_RGB555 ||
- fmt->palette == VIDEO_PALETTE_RGB565 ||
- fmt->palette == VIDEO_PALETTE_RGB32) {
- fh->ovfmt = fmt+1;
- }
- }
- bt848_bright(btv,pic->brightness);
- bt848_contrast(btv,pic->contrast);
- bt848_hue(btv,pic->hue);
- bt848_sat(btv,pic->colour);
+ return setup_window(fh, btv, &f->fmt.win, 1);
+}
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
+{
+ int retval;
+ unsigned int i;
+ struct bttv_fh *fh = priv;
+
+ mutex_lock(&fh->cap.lock);
+ retval = videobuf_mmap_setup(&fh->cap, gbuffers, gbufsize,
+ V4L2_MEMORY_MMAP);
+ if (retval < 0) {
mutex_unlock(&fh->cap.lock);
- return 0;
+ return retval;
}
- case VIDIOCGWIN:
- {
- struct video_window *win = arg;
+ gbuffers = retval;
+ memset(mbuf, 0, sizeof(*mbuf));
+ mbuf->frames = gbuffers;
+ mbuf->size = gbuffers * gbufsize;
- memset(win,0,sizeof(*win));
- win->x = fh->ov.w.left;
- win->y = fh->ov.w.top;
- win->width = fh->ov.w.width;
- win->height = fh->ov.w.height;
- return 0;
- }
- case VIDIOCSWIN:
- {
- struct video_window *win = arg;
- struct v4l2_window w2;
+ for (i = 0; i < gbuffers; i++)
+ mbuf->offsets[i] = i * gbufsize;
- if (no_overlay > 0) {
- printk ("VIDIOCSWIN: no_overlay\n");
- return -EINVAL;
- }
+ mutex_unlock(&fh->cap.lock);
+ return 0;
+}
+#endif
- w2.field = V4L2_FIELD_ANY;
- w2.w.left = win->x;
- w2.w.top = win->y;
- w2.w.width = win->width;
- w2.w.height = win->height;
- w2.clipcount = win->clipcount;
- w2.clips = (struct v4l2_clip __user *)win->clips;
- retval = setup_window(fh, btv, &w2, 0);
- if (0 == retval) {
- /* on v4l1 this ioctl affects the read() size too */
- fh->width = fh->ov.w.width;
- fh->height = fh->ov.w.height;
- btv->init.width = fh->ov.w.width;
- btv->init.height = fh->ov.w.height;
- }
- return retval;
- }
+static int bttv_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- case VIDIOCGFBUF:
- {
- struct video_buffer *fbuf = arg;
-
- fbuf->base = btv->fbuf.base;
- fbuf->width = btv->fbuf.fmt.width;
- fbuf->height = btv->fbuf.fmt.height;
- fbuf->bytesperline = btv->fbuf.fmt.bytesperline;
- if (fh->ovfmt)
- fbuf->depth = fh->ovfmt->depth;
- else {
- if (fbuf->width)
- fbuf->depth = ((fbuf->bytesperline<<3)
- + (fbuf->width-1) )
- /fbuf->width;
- else
- fbuf->depth = 0;
- }
- return 0;
- }
- case VIDIOCSFBUF:
- {
- struct video_buffer *fbuf = arg;
- const struct bttv_format *fmt;
- unsigned long end;
-
- if(!capable(CAP_SYS_ADMIN) &&
- !capable(CAP_SYS_RAWIO))
- return -EPERM;
- end = (unsigned long)fbuf->base +
- fbuf->height * fbuf->bytesperline;
- mutex_lock(&fh->cap.lock);
- retval = -EINVAL;
+ if (0 == v4l2)
+ return -EINVAL;
- switch (fbuf->depth) {
- case 8:
- fmt = format_by_palette(VIDEO_PALETTE_HI240);
- break;
- case 16:
- fmt = format_by_palette(VIDEO_PALETTE_RGB565);
- break;
- case 24:
- fmt = format_by_palette(VIDEO_PALETTE_RGB24);
- break;
- case 32:
- fmt = format_by_palette(VIDEO_PALETTE_RGB32);
- break;
- case 15:
- fbuf->depth = 16;
- fmt = format_by_palette(VIDEO_PALETTE_RGB555);
- break;
- default:
- fmt = NULL;
- break;
- }
- if (NULL == fmt)
- goto fh_unlock_and_return;
-
- fh->ovfmt = fmt;
- fh->fmt = fmt;
- btv->init.ovfmt = fmt;
- btv->init.fmt = fmt;
- btv->fbuf.base = fbuf->base;
- btv->fbuf.fmt.width = fbuf->width;
- btv->fbuf.fmt.height = fbuf->height;
- if (fbuf->bytesperline)
- btv->fbuf.fmt.bytesperline = fbuf->bytesperline;
- else
- btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fbuf->depth/8;
- mutex_unlock(&fh->cap.lock);
- return 0;
- }
+ strlcpy(cap->driver, "bttv", sizeof(cap->driver));
+ strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "PCI:%s", pci_name(btv->c.pci));
+ cap->version = BTTV_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ if (no_overlay <= 0)
+ cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
+
+ if (bttv_tvcards[btv->c.type].tuner != UNSET &&
+ bttv_tvcards[btv->c.type].tuner != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
- case VIDIOCCAPTURE:
- case VIDIOC_OVERLAY:
- {
- struct bttv_buffer *new;
- int *on = arg;
+static int bttv_enum_fmt_vbi(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (0 != f->index)
+ return -EINVAL;
- if (*on) {
- /* verify args */
- if (NULL == btv->fbuf.base)
- return -EINVAL;
- if (!fh->ov.setup_ok) {
- dprintk("bttv%d: overlay: !setup_ok\n",btv->c.nr);
- return -EINVAL;
- }
- }
+ f->pixelformat = V4L2_PIX_FMT_GREY;
+ strcpy(f->description, "vbi data");
- if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY))
- return -EBUSY;
+ return 0;
+}
- mutex_lock(&fh->cap.lock);
- if (*on) {
- fh->ov.tvnorm = btv->tvnorm;
- new = videobuf_pci_alloc(sizeof(*new));
- new->crop = btv->crop[!!fh->do_crop].rect;
- bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
- } else {
- new = NULL;
- }
+static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
+{
+ int index = -1, i;
- /* switch over */
- retval = bttv_switch_overlay(btv,fh,new);
- mutex_unlock(&fh->cap.lock);
- return retval;
+ for (i = 0; i < FORMATS; i++) {
+ if (formats[i].fourcc != -1)
+ index++;
+ if ((unsigned int)index == f->index)
+ break;
}
+ if (FORMATS == i)
+ return -EINVAL;
- case VIDIOCGMBUF:
- {
- struct video_mbuf *mbuf = arg;
- unsigned int i;
+ f->pixelformat = formats[i].fourcc;
+ strlcpy(f->description, formats[i].name, sizeof(f->description));
- retval = videobuf_mmap_setup(&fh->cap,gbuffers,gbufsize,
- V4L2_MEMORY_MMAP);
- if (retval < 0)
- return retval;
+ return i;
+}
- gbuffers = retval;
- memset(mbuf,0,sizeof(*mbuf));
- mbuf->frames = gbuffers;
- mbuf->size = gbuffers * gbufsize;
- for (i = 0; i < gbuffers; i++)
- mbuf->offsets[i] = i * gbufsize;
- return 0;
- }
- case VIDIOCMCAPTURE:
- {
- struct video_mmap *vm = arg;
- struct bttv_buffer *buf;
- enum v4l2_field field;
- __s32 height2;
- int res;
+static int bttv_enum_fmt_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int rc = bttv_enum_fmt_cap_ovr(f);
- if (vm->frame >= VIDEO_MAX_FRAME)
- return -EINVAL;
+ if (rc < 0)
+ return rc;
- res = bttv_resource(fh);
- if (!check_alloc_btres(btv, fh, res))
- return -EBUSY;
+ return 0;
+}
- mutex_lock(&fh->cap.lock);
- retval = -EINVAL;
- buf = (struct bttv_buffer *)fh->cap.bufs[vm->frame];
- if (NULL == buf)
- goto fh_unlock_and_return;
- if (0 == buf->vb.baddr)
- goto fh_unlock_and_return;
- if (buf->vb.state == STATE_QUEUED ||
- buf->vb.state == STATE_ACTIVE)
- goto fh_unlock_and_return;
+static int bttv_enum_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int rc;
- height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
- field = (vm->height > height2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- retval = bttv_prepare_buffer(&fh->cap,btv,buf,
- format_by_palette(vm->format),
- vm->width,vm->height,field);
- if (0 != retval)
- goto fh_unlock_and_return;
- btv->init.width = vm->width;
- btv->init.height = vm->height;
- spin_lock_irqsave(&btv->s_lock,flags);
- buffer_queue(&fh->cap,&buf->vb);
- spin_unlock_irqrestore(&btv->s_lock,flags);
- mutex_unlock(&fh->cap.lock);
- return 0;
+ if (no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
}
- case VIDIOCSYNC:
- {
- int *frame = arg;
- struct bttv_buffer *buf;
- if (*frame >= VIDEO_MAX_FRAME)
- return -EINVAL;
+ rc = bttv_enum_fmt_cap_ovr(f);
- mutex_lock(&fh->cap.lock);
- retval = -EINVAL;
- buf = (struct bttv_buffer *)fh->cap.bufs[*frame];
- if (NULL == buf)
- goto fh_unlock_and_return;
- retval = videobuf_waiton(&buf->vb,0,1);
- if (0 != retval)
- goto fh_unlock_and_return;
- switch (buf->vb.state) {
- case STATE_ERROR:
- retval = -EIO;
- /* fall through */
- case STATE_DONE:
- {
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- videobuf_dma_sync(&fh->cap,dma);
- bttv_dma_free(&fh->cap,btv,buf);
- break;
- }
- default:
- retval = -EINVAL;
- break;
- }
- mutex_unlock(&fh->cap.lock);
- return retval;
- }
+ if (rc < 0)
+ return rc;
- case VIDIOCGVBIFMT:
- if (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) {
- retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE);
- if (0 != retval)
- return retval;
- }
+ if (!(formats[rc].flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
- /* fall through */
+ return 0;
+}
- case VIDIOCSVBIFMT:
- return v4l_compat_translate_ioctl(inode, file, cmd,
- arg, bttv_do_ioctl);
-
- case BTTV_VERSION:
- case VIDIOCGFREQ:
- case VIDIOCSFREQ:
- case VIDIOCGTUNER:
- case VIDIOCSTUNER:
- case VIDIOCGCHAN:
- case VIDIOCSCHAN:
- case VIDIOCGAUDIO:
- case VIDIOCSAUDIO:
- return bttv_common_ioctls(btv,cmd,arg);
-
- /* *** v4l2 *** ************************************************ */
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
+static int bttv_g_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- if (0 == v4l2)
- return -EINVAL;
- memset(cap, 0, sizeof (*cap));
- strlcpy(cap->driver, "bttv", sizeof (cap->driver));
- strlcpy(cap->card, btv->video_dev->name, sizeof (cap->card));
- snprintf(cap->bus_info, sizeof (cap->bus_info),
- "PCI:%s", pci_name(btv->c.pci));
- cap->version = BTTV_VERSION_CODE;
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VBI_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- if (no_overlay <= 0)
- cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
-
- if (bttv_tvcards[btv->c.type].tuner != UNSET &&
- bttv_tvcards[btv->c.type].tuner != TUNER_ABSENT)
- cap->capabilities |= V4L2_CAP_TUNER;
- return 0;
- }
+ *fb = btv->fbuf;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ if (fh->ovfmt)
+ fb->fmt.pixelformat = fh->ovfmt->fourcc;
+ return 0;
+}
- case VIDIOC_ENUM_FMT:
- {
- struct v4l2_fmtdesc *f = arg;
- enum v4l2_buf_type type;
- unsigned int i;
- int index;
-
- type = f->type;
- if (V4L2_BUF_TYPE_VBI_CAPTURE == type) {
- /* vbi */
- index = f->index;
- if (0 != index)
- return -EINVAL;
- memset(f,0,sizeof(*f));
- f->index = index;
- f->type = type;
- f->pixelformat = V4L2_PIX_FMT_GREY;
- strcpy(f->description,"vbi data");
- return 0;
- }
+static int bttv_overlay(struct file *file, void *f, unsigned int on)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ struct bttv_buffer *new;
+ int retval;
- /* video capture + overlay */
- index = -1;
- for (i = 0; i < BTTV_FORMATS; i++) {
- if (bttv_formats[i].fourcc != -1)
- index++;
- if ((unsigned int)index == f->index)
- break;
- }
- if (BTTV_FORMATS == i)
+ if (on) {
+ /* verify args */
+ if (NULL == btv->fbuf.base)
return -EINVAL;
-
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (!(bttv_formats[i].flags & FORMAT_FLAGS_PACKED))
- return -EINVAL;
- break;
- default:
+ if (!fh->ov.setup_ok) {
+ dprintk("bttv%d: overlay: !setup_ok\n", btv->c.nr);
return -EINVAL;
}
- memset(f,0,sizeof(*f));
- f->index = index;
- f->type = type;
- f->pixelformat = bttv_formats[i].fourcc;
- strlcpy(f->description,bttv_formats[i].name,sizeof(f->description));
- return 0;
}
- case VIDIOC_TRY_FMT:
- {
- struct v4l2_format *f = arg;
- return bttv_try_fmt(fh,btv,f, /* adjust_crop */ 0);
- }
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *f = arg;
- return bttv_g_fmt(fh,f);
- }
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *f = arg;
- return bttv_s_fmt(fh,btv,f);
+ if (!check_alloc_btres(btv, fh, RESOURCE_OVERLAY))
+ return -EBUSY;
+
+ mutex_lock(&fh->cap.lock);
+ if (on) {
+ fh->ov.tvnorm = btv->tvnorm;
+ new = videobuf_pci_alloc(sizeof(*new));
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ } else {
+ new = NULL;
}
- case VIDIOC_G_FBUF:
- {
- struct v4l2_framebuffer *fb = arg;
+ /* switch over */
+ retval = bttv_switch_overlay(btv, fh, new);
+ mutex_unlock(&fh->cap.lock);
+ return retval;
+}
- *fb = btv->fbuf;
- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
- if (fh->ovfmt)
- fb->fmt.pixelformat = fh->ovfmt->fourcc;
- return 0;
- }
- case VIDIOC_S_FBUF:
- {
- struct v4l2_framebuffer *fb = arg;
- const struct bttv_format *fmt;
+static int bttv_s_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct bttv_format *fmt;
+ int retval;
- if(!capable(CAP_SYS_ADMIN) &&
- !capable(CAP_SYS_RAWIO))
- return -EPERM;
+ if (!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
+ return -EPERM;
- /* check args */
- fmt = format_by_fourcc(fb->fmt.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
- if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
- return -EINVAL;
+ /* check args */
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+ if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
- retval = -EINVAL;
- if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
- __s32 width = fb->fmt.width;
- __s32 height = fb->fmt.height;
-
- retval = limit_scaled_size(fh, &width, &height,
- V4L2_FIELD_INTERLACED,
- /* width_mask */ ~3,
- /* width_bias */ 2,
- /* adjust_size */ 0,
- /* adjust_crop */ 0);
- if (0 != retval)
- return retval;
- }
+ retval = -EINVAL;
+ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+ __s32 width = fb->fmt.width;
+ __s32 height = fb->fmt.height;
+
+ retval = limit_scaled_size(fh, &width, &height,
+ V4L2_FIELD_INTERLACED,
+ /* width_mask */ ~3,
+ /* width_bias */ 2,
+ /* adjust_size */ 0,
+ /* adjust_crop */ 0);
+ if (0 != retval)
+ return retval;
+ }
- /* ok, accept it */
- mutex_lock(&fh->cap.lock);
- btv->fbuf.base = fb->base;
- btv->fbuf.fmt.width = fb->fmt.width;
- btv->fbuf.fmt.height = fb->fmt.height;
- if (0 != fb->fmt.bytesperline)
- btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
- else
- btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
-
- retval = 0;
- fh->ovfmt = fmt;
- btv->init.ovfmt = fmt;
- if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
- fh->ov.w.left = 0;
- fh->ov.w.top = 0;
- fh->ov.w.width = fb->fmt.width;
- fh->ov.w.height = fb->fmt.height;
- btv->init.ov.w.width = fb->fmt.width;
- btv->init.ov.w.height = fb->fmt.height;
- kfree(fh->ov.clips);
- fh->ov.clips = NULL;
- fh->ov.nclips = 0;
-
- if (check_btres(fh, RESOURCE_OVERLAY)) {
- struct bttv_buffer *new;
-
- new = videobuf_pci_alloc(sizeof(*new));
- new->crop = btv->crop[!!fh->do_crop].rect;
- bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new);
- retval = bttv_switch_overlay(btv,fh,new);
- }
+ /* ok, accept it */
+ mutex_lock(&fh->cap.lock);
+ btv->fbuf.base = fb->base;
+ btv->fbuf.fmt.width = fb->fmt.width;
+ btv->fbuf.fmt.height = fb->fmt.height;
+ if (0 != fb->fmt.bytesperline)
+ btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
+ else
+ btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
+
+ retval = 0;
+ fh->ovfmt = fmt;
+ btv->init.ovfmt = fmt;
+ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+ fh->ov.w.left = 0;
+ fh->ov.w.top = 0;
+ fh->ov.w.width = fb->fmt.width;
+ fh->ov.w.height = fb->fmt.height;
+ btv->init.ov.w.width = fb->fmt.width;
+ btv->init.ov.w.height = fb->fmt.height;
+ kfree(fh->ov.clips);
+ fh->ov.clips = NULL;
+ fh->ov.nclips = 0;
+
+ if (check_btres(fh, RESOURCE_OVERLAY)) {
+ struct bttv_buffer *new;
+
+ new = videobuf_pci_alloc(sizeof(*new));
+ new->crop = btv->crop[!!fh->do_crop].rect;
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ retval = bttv_switch_overlay(btv, fh, new);
}
- mutex_unlock(&fh->cap.lock);
- return retval;
}
+ mutex_unlock(&fh->cap.lock);
+ return retval;
+}
- case VIDIOC_REQBUFS:
- return videobuf_reqbufs(bttv_queue(fh),arg);
+static int bttv_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_reqbufs(bttv_queue(fh), p);
+}
- case VIDIOC_QUERYBUF:
- return videobuf_querybuf(bttv_queue(fh),arg);
+static int bttv_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_querybuf(bttv_queue(fh), b);
+}
- case VIDIOC_QBUF:
- {
- int res = bttv_resource(fh);
+static int bttv_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int res = bttv_resource(fh);
- if (!check_alloc_btres(btv, fh, res))
- return -EBUSY;
- return videobuf_qbuf(bttv_queue(fh),arg);
- }
+ if (!check_alloc_btres(btv, fh, res))
+ return -EBUSY;
- case VIDIOC_DQBUF:
- return videobuf_dqbuf(bttv_queue(fh),arg,
- file->f_flags & O_NONBLOCK);
+ return videobuf_qbuf(bttv_queue(fh), b);
+}
- case VIDIOC_STREAMON:
- {
- int res = bttv_resource(fh);
+static int bttv_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_dqbuf(bttv_queue(fh), b,
+ file->f_flags & O_NONBLOCK);
+}
- if (!check_alloc_btres(btv,fh,res))
- return -EBUSY;
- return videobuf_streamon(bttv_queue(fh));
- }
- case VIDIOC_STREAMOFF:
- {
- int res = bttv_resource(fh);
+static int bttv_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int res = bttv_resource(fh);
- retval = videobuf_streamoff(bttv_queue(fh));
- if (retval < 0)
- return retval;
- free_btres(btv,fh,res);
- return 0;
- }
+ if (!check_alloc_btres(btv, fh, res))
+ return -EBUSY;
+ return videobuf_streamon(bttv_queue(fh));
+}
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *c = arg;
- int i;
- if ((c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1) &&
- (c->id < V4L2_CID_PRIVATE_BASE ||
- c->id >= V4L2_CID_PRIVATE_LASTP1))
- return -EINVAL;
- for (i = 0; i < BTTV_CTLS; i++)
- if (bttv_ctls[i].id == c->id)
- break;
- if (i == BTTV_CTLS) {
- *c = no_ctl;
- return 0;
- }
- *c = bttv_ctls[i];
- if (btv->audio_hook && i >= 4 && i <= 8) {
- struct video_audio va;
- memset(&va,0,sizeof(va));
- btv->audio_hook(btv,&va,0);
- switch (bttv_ctls[i].id) {
- case V4L2_CID_AUDIO_VOLUME:
- if (!(va.flags & VIDEO_AUDIO_VOLUME))
- *c = no_ctl;
- break;
- case V4L2_CID_AUDIO_BALANCE:
- if (!(va.flags & VIDEO_AUDIO_BALANCE))
- *c = no_ctl;
- break;
- case V4L2_CID_AUDIO_BASS:
- if (!(va.flags & VIDEO_AUDIO_BASS))
- *c = no_ctl;
- break;
- case V4L2_CID_AUDIO_TREBLE:
- if (!(va.flags & VIDEO_AUDIO_TREBLE))
- *c = no_ctl;
- break;
- }
- }
- return 0;
- }
- case VIDIOC_G_CTRL:
- return get_control(btv,arg);
- case VIDIOC_S_CTRL:
- return set_control(btv,arg);
- case VIDIOC_G_PARM:
- {
- struct v4l2_streamparm *parm = arg;
- struct v4l2_standard s;
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- memset(parm,0,sizeof(*parm));
- v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id,
- bttv_tvnorms[btv->tvnorm].name);
- parm->parm.capture.timeperframe = s.frameperiod;
- return 0;
- }
+static int bttv_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int retval;
+ int res = bttv_resource(fh);
- case VIDIOC_G_PRIORITY:
- {
- enum v4l2_priority *p = arg;
- *p = v4l2_prio_max(&btv->prio);
- return 0;
- }
- case VIDIOC_S_PRIORITY:
- {
- enum v4l2_priority *prio = arg;
+ retval = videobuf_streamoff(bttv_queue(fh));
+ if (retval < 0)
+ return retval;
+ free_btres(btv, fh, res);
+ return 0;
+}
- return v4l2_prio_change(&btv->prio, &fh->prio, *prio);
- }
+static int bttv_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ const struct v4l2_queryctrl *ctrl;
- case VIDIOC_CROPCAP:
- {
- struct v4l2_cropcap *cap = arg;
- enum v4l2_buf_type type;
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1) &&
+ (c->id < V4L2_CID_PRIVATE_BASE ||
+ c->id >= V4L2_CID_PRIVATE_LASTP1))
+ return -EINVAL;
- type = cap->type;
+ if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME))
+ *c = no_ctl;
+ else {
+ ctrl = ctrl_by_id(c->id);
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
+ *c = (NULL != ctrl) ? *ctrl : no_ctl;
+ }
- *cap = bttv_tvnorms[btv->tvnorm].cropcap;
- cap->type = type;
+ return 0;
+}
- return 0;
- }
- case VIDIOC_G_CROP:
- {
- struct v4l2_crop * crop = arg;
+static int bttv_g_parm(struct file *file, void *f,
+ struct v4l2_streamparm *parm)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ struct v4l2_standard s;
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id,
+ bttv_tvnorms[btv->tvnorm].name);
+ parm->parm.capture.timeperframe = s.frameperiod;
+ return 0;
+}
- /* No fh->do_crop = 1; because btv->crop[1] may be
- inconsistent with fh->width or fh->height and apps
- do not expect a change here. */
+static int bttv_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- crop->c = btv->crop[!!fh->do_crop].rect;
+ if (UNSET == bttv_tvcards[btv->c.type].tuner)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
- return 0;
- }
- case VIDIOC_S_CROP:
- {
- struct v4l2_crop *crop = arg;
- const struct v4l2_rect *b;
- struct bttv_crop c;
- __s32 b_left;
- __s32 b_top;
- __s32 b_right;
- __s32 b_bottom;
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
+ mutex_lock(&btv->lock);
+ memset(t, 0, sizeof(*t));
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t);
+ strcpy(t->name, "Television");
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->type = V4L2_TUNER_ANALOG_TV;
+ if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
+ t->signal = 0xffff;
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 0);
- retval = v4l2_prio_check(&btv->prio,&fh->prio);
- if (0 != retval)
- return retval;
+ mutex_unlock(&btv->lock);
+ return 0;
+}
- /* Make sure tvnorm, vbi_end and the current cropping
- parameters remain consistent until we're done. Note
- read() may change vbi_end in check_alloc_btres(). */
- mutex_lock(&btv->lock);
+static int bttv_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- retval = -EBUSY;
+ *p = v4l2_prio_max(&btv->prio);
- if (locked_btres(fh->btv, VIDEO_RESOURCES))
- goto btv_unlock_and_return;
+ return 0;
+}
- b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+static int bttv_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- b_left = b->left;
- b_right = b_left + b->width;
- b_bottom = b->top + b->height;
+ return v4l2_prio_change(&btv->prio, &fh->prio, prio);
+}
- b_top = max(b->top, btv->vbi_end);
- if (b_top + 32 >= b_bottom)
- goto btv_unlock_and_return;
+static int bttv_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- /* Min. scaled size 48 x 32. */
- c.rect.left = clamp(crop->c.left, b_left, b_right - 48);
- c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
- c.rect.width = clamp(crop->c.width,
- 48, b_right - c.rect.left);
+ *cap = bttv_tvnorms[btv->tvnorm].cropcap;
- c.rect.top = clamp(crop->c.top, b_top, b_bottom - 32);
- /* Top and height must be a multiple of two. */
- c.rect.top = (c.rect.top + 1) & ~1;
+ return 0;
+}
- c.rect.height = clamp(crop->c.height,
- 32, b_bottom - c.rect.top);
- c.rect.height = (c.rect.height + 1) & ~1;
+static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
- bttv_crop_calc_limits(&c);
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
- btv->crop[1] = c;
+ /* No fh->do_crop = 1; because btv->crop[1] may be
+ inconsistent with fh->width or fh->height and apps
+ do not expect a change here. */
- mutex_unlock(&btv->lock);
+ crop->c = btv->crop[!!fh->do_crop].rect;
- fh->do_crop = 1;
+ return 0;
+}
- mutex_lock(&fh->cap.lock);
+static int bttv_s_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct v4l2_rect *b;
+ int retval;
+ struct bttv_crop c;
+ __s32 b_left;
+ __s32 b_top;
+ __s32 b_right;
+ __s32 b_bottom;
- if (fh->width < c.min_scaled_width) {
- fh->width = c.min_scaled_width;
- btv->init.width = c.min_scaled_width;
- } else if (fh->width > c.max_scaled_width) {
- fh->width = c.max_scaled_width;
- btv->init.width = c.max_scaled_width;
- }
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
- if (fh->height < c.min_scaled_height) {
- fh->height = c.min_scaled_height;
- btv->init.height = c.min_scaled_height;
- } else if (fh->height > c.max_scaled_height) {
- fh->height = c.max_scaled_height;
- btv->init.height = c.max_scaled_height;
- }
+ retval = v4l2_prio_check(&btv->prio, &fh->prio);
+ if (0 != retval)
+ return retval;
- mutex_unlock(&fh->cap.lock);
+ /* Make sure tvnorm, vbi_end and the current cropping
+ parameters remain consistent until we're done. Note
+ read() may change vbi_end in check_alloc_btres(). */
+ mutex_lock(&btv->lock);
- return 0;
+ retval = -EBUSY;
+
+ if (locked_btres(fh->btv, VIDEO_RESOURCES)) {
+ mutex_unlock(&btv->lock);
+ return retval;
}
- case VIDIOC_ENUMSTD:
- case VIDIOC_G_STD:
- case VIDIOC_S_STD:
- case VIDIOC_ENUMINPUT:
- case VIDIOC_G_INPUT:
- case VIDIOC_S_INPUT:
- case VIDIOC_G_TUNER:
- case VIDIOC_S_TUNER:
- case VIDIOC_G_FREQUENCY:
- case VIDIOC_S_FREQUENCY:
- case VIDIOC_LOG_STATUS:
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- return bttv_common_ioctls(btv,cmd,arg);
+ b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
- default:
- return -ENOIOCTLCMD;
+ b_left = b->left;
+ b_right = b_left + b->width;
+ b_bottom = b->top + b->height;
+
+ b_top = max(b->top, btv->vbi_end);
+ if (b_top + 32 >= b_bottom) {
+ mutex_unlock(&btv->lock);
+ return retval;
}
- return 0;
- fh_unlock_and_return:
- mutex_unlock(&fh->cap.lock);
- return retval;
+ /* Min. scaled size 48 x 32. */
+ c.rect.left = clamp(crop->c.left, b_left, b_right - 48);
+ c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
- btv_unlock_and_return:
- mutex_unlock(&btv->lock);
- return retval;
-}
+ c.rect.width = clamp(crop->c.width,
+ 48, b_right - c.rect.left);
-static int bttv_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct bttv_fh *fh = file->private_data;
+ c.rect.top = clamp(crop->c.top, b_top, b_bottom - 32);
+ /* Top and height must be a multiple of two. */
+ c.rect.top = (c.rect.top + 1) & ~1;
- switch (cmd) {
- case BTTV_VBISIZE:
- {
- const struct bttv_tvnorm *tvnorm;
+ c.rect.height = clamp(crop->c.height,
+ 32, b_bottom - c.rect.top);
+ c.rect.height = (c.rect.height + 1) & ~1;
- tvnorm = fh->vbi_fmt.tvnorm;
+ bttv_crop_calc_limits(&c);
- if (fh->vbi_fmt.fmt.start[0] != tvnorm->vbistart[0] ||
- fh->vbi_fmt.fmt.start[1] != tvnorm->vbistart[1] ||
- fh->vbi_fmt.fmt.count[0] != fh->vbi_fmt.fmt.count[1]) {
- /* BTTV_VBISIZE cannot express these parameters,
- however open() resets the paramters to defaults
- and apps shouldn't call BTTV_VBISIZE after
- VIDIOC_S_FMT. */
- return -EINVAL;
- }
+ btv->crop[1] = c;
+
+ mutex_unlock(&btv->lock);
+
+ fh->do_crop = 1;
- bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE);
- return (fh->vbi_fmt.fmt.count[0] * 2
- * fh->vbi_fmt.fmt.samples_per_line);
+ mutex_lock(&fh->cap.lock);
+
+ if (fh->width < c.min_scaled_width) {
+ fh->width = c.min_scaled_width;
+ btv->init.width = c.min_scaled_width;
+ } else if (fh->width > c.max_scaled_width) {
+ fh->width = c.max_scaled_width;
+ btv->init.width = c.max_scaled_width;
}
- default:
- return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl);
+ if (fh->height < c.min_scaled_height) {
+ fh->height = c.min_scaled_height;
+ btv->init.height = c.min_scaled_height;
+ } else if (fh->height > c.max_scaled_height) {
+ fh->height = c.max_scaled_height;
+ btv->init.height = c.max_scaled_height;
}
+
+ mutex_unlock(&fh->cap.lock);
+
+ return 0;
+}
+
+static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ strcpy(a->name, "audio");
+ return 0;
+}
+
+static int bttv_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ return 0;
}
static ssize_t bttv_read(struct file *file, char __user *data,
@@ -3719,8 +3205,8 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait)
}
poll_wait(file, &buf->vb.done, wait);
- if (buf->vb.state == STATE_DONE ||
- buf->vb.state == STATE_ERROR)
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
return POLLIN|POLLRDNORM;
return 0;
}
@@ -3777,7 +3263,7 @@ static int bttv_open(struct inode *inode, struct file *file)
V4L2_FIELD_SEQ_TB,
sizeof(struct bttv_buffer),
fh);
- i2c_vidiocschan(btv);
+ set_tvnorm(btv,btv->tvnorm);
btv->users++;
@@ -3857,7 +3343,7 @@ static const struct file_operations bttv_fops =
.owner = THIS_MODULE,
.open = bttv_open,
.release = bttv_release,
- .ioctl = bttv_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
.read = bttv_read,
@@ -3867,19 +3353,61 @@ static const struct file_operations bttv_fops =
static struct video_device bttv_video_template =
{
- .name = "UNSET",
- .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|
- VID_TYPE_CLIPPING|VID_TYPE_SCALES,
- .fops = &bttv_fops,
- .minor = -1,
-};
-
-static struct video_device bttv_vbi_template =
-{
- .name = "bt848/878 vbi",
- .type = VID_TYPE_TUNER|VID_TYPE_TELETEXT,
.fops = &bttv_fops,
.minor = -1,
+ .vidioc_querycap = bttv_querycap,
+ .vidioc_enum_fmt_cap = bttv_enum_fmt_cap,
+ .vidioc_g_fmt_cap = bttv_g_fmt_cap,
+ .vidioc_try_fmt_cap = bttv_try_fmt_cap,
+ .vidioc_s_fmt_cap = bttv_s_fmt_cap,
+ .vidioc_enum_fmt_overlay = bttv_enum_fmt_overlay,
+ .vidioc_g_fmt_overlay = bttv_g_fmt_overlay,
+ .vidioc_try_fmt_overlay = bttv_try_fmt_overlay,
+ .vidioc_s_fmt_overlay = bttv_s_fmt_overlay,
+ .vidioc_enum_fmt_vbi = bttv_enum_fmt_vbi,
+ .vidioc_g_fmt_vbi = bttv_g_fmt_vbi,
+ .vidioc_try_fmt_vbi = bttv_try_fmt_vbi,
+ .vidioc_s_fmt_vbi = bttv_s_fmt_vbi,
+ .vidioc_g_audio = bttv_g_audio,
+ .vidioc_s_audio = bttv_s_audio,
+ .vidioc_cropcap = bttv_cropcap,
+ .vidioc_reqbufs = bttv_reqbufs,
+ .vidioc_querybuf = bttv_querybuf,
+ .vidioc_qbuf = bttv_qbuf,
+ .vidioc_dqbuf = bttv_dqbuf,
+ .vidioc_s_std = bttv_s_std,
+ .vidioc_enum_input = bttv_enum_input,
+ .vidioc_g_input = bttv_g_input,
+ .vidioc_s_input = bttv_s_input,
+ .vidioc_queryctrl = bttv_queryctrl,
+ .vidioc_g_ctrl = bttv_g_ctrl,
+ .vidioc_s_ctrl = bttv_s_ctrl,
+ .vidioc_streamon = bttv_streamon,
+ .vidioc_streamoff = bttv_streamoff,
+ .vidioc_g_tuner = bttv_g_tuner,
+ .vidioc_s_tuner = bttv_s_tuner,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ .vidiocgmbuf = vidiocgmbuf,
+#endif
+ .vidioc_g_crop = bttv_g_crop,
+ .vidioc_g_crop = bttv_g_crop,
+ .vidioc_s_crop = bttv_s_crop,
+ .vidioc_g_fbuf = bttv_g_fbuf,
+ .vidioc_s_fbuf = bttv_s_fbuf,
+ .vidioc_overlay = bttv_overlay,
+ .vidioc_g_priority = bttv_g_priority,
+ .vidioc_s_priority = bttv_s_priority,
+ .vidioc_g_parm = bttv_g_parm,
+ .vidioc_g_frequency = bttv_g_frequency,
+ .vidioc_s_frequency = bttv_s_frequency,
+ .vidioc_log_status = bttv_log_status,
+ .vidioc_querystd = bttv_querystd,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = bttv_g_register,
+ .vidioc_s_register = bttv_s_register,
+#endif
+ .tvnorms = BTTV_NORMS,
+ .current_norm = V4L2_STD_PAL,
};
/* ----------------------------------------------------------------------- */
@@ -3918,7 +3446,7 @@ static int radio_open(struct inode *inode, struct file *file)
static int radio_release(struct inode *inode, struct file *file)
{
- struct bttv *btv = file->private_data;
+ struct bttv *btv = file->private_data;
struct rds_command cmd;
btv->radio_user--;
@@ -3928,59 +3456,116 @@ static int radio_release(struct inode *inode, struct file *file)
return 0;
}
-static int radio_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
{
- struct bttv *btv = file->private_data;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- switch (cmd) {
- case VIDIOCGCAP:
- {
- struct video_capability *cap = arg;
+ strcpy(cap->driver, "bttv");
+ strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci));
+ cap->version = BTTV_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_TUNER;
- memset(cap,0,sizeof(*cap));
- strcpy(cap->name,btv->radio_dev->name);
- cap->type = VID_TYPE_TUNER;
- cap->channels = 1;
- cap->audios = 1;
- return 0;
- }
+ return 0;
+}
- case VIDIOCGTUNER:
- {
- struct video_tuner *v = arg;
+static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
- if(v->tuner)
- return -EINVAL;
- memset(v,0,sizeof(*v));
- strcpy(v->name, "Radio");
- bttv_call_i2c_clients(btv,cmd,v);
- return 0;
- }
- case VIDIOCSTUNER:
- /* nothing to do */
- return 0;
+ if (UNSET == bttv_tvcards[btv->c.type].tuner)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+ mutex_lock(&btv->lock);
+ memset(t, 0, sizeof(*t));
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
- case BTTV_VERSION:
- case VIDIOCGFREQ:
- case VIDIOCSFREQ:
- case VIDIOCGAUDIO:
- case VIDIOCSAUDIO:
- case VIDIOC_LOG_STATUS:
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- return bttv_common_ioctls(btv,cmd,arg);
+ bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t);
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 0);
+
+ mutex_unlock(&btv->lock);
+
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ memset(a, 0, sizeof(*a));
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t);
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ const struct v4l2_queryctrl *ctrl;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl = ctrl_by_id(c->id);
+ *c = *ctrl;
+ } else
+ *c = no_ctl;
- default:
- return -ENOIOCTLCMD;
- }
return 0;
}
-static int radio_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
{
- return video_usercopy(inode, file, cmd, arg, radio_do_ioctl);
+ *i = 0;
+ return 0;
}
static ssize_t radio_read(struct file *file, char __user *data,
@@ -4016,17 +3601,29 @@ static const struct file_operations radio_fops =
.open = radio_open,
.read = radio_read,
.release = radio_release,
- .ioctl = radio_ioctl,
+ .ioctl = video_ioctl2,
.llseek = no_llseek,
.poll = radio_poll,
};
static struct video_device radio_template =
{
- .name = "bt848/878 radio",
- .type = VID_TYPE_TUNER,
.fops = &radio_fops,
.minor = -1,
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_s_std = radio_s_std,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_input = radio_g_input,
+ .vidioc_g_ctrl = bttv_g_ctrl,
+ .vidioc_s_ctrl = bttv_s_ctrl,
+ .vidioc_g_frequency = bttv_g_frequency,
+ .vidioc_s_frequency = bttv_s_frequency,
};
/* ----------------------------------------------------------------------- */
@@ -4308,20 +3905,20 @@ static void bttv_irq_timeout(unsigned long data)
bttv_set_dma(btv, 0);
/* wake up */
- bttv_irq_wakeup_video(btv, &old, &new, STATE_ERROR);
- bttv_irq_wakeup_vbi(btv, ovbi, STATE_ERROR);
+ bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_ERROR);
+ bttv_irq_wakeup_vbi(btv, ovbi, VIDEOBUF_ERROR);
/* cancel all outstanding capture / vbi requests */
while (!list_empty(&btv->capture)) {
item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
list_del(&item->vb.queue);
- item->vb.state = STATE_ERROR;
+ item->vb.state = VIDEOBUF_ERROR;
wake_up(&item->vb.done);
}
while (!list_empty(&btv->vcapture)) {
item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
list_del(&item->vb.queue);
- item->vb.state = STATE_ERROR;
+ item->vb.state = VIDEOBUF_ERROR;
wake_up(&item->vb.done);
}
@@ -4344,7 +3941,7 @@ bttv_irq_wakeup_top(struct bttv *btv)
do_gettimeofday(&wakeup->vb.ts);
wakeup->vb.field_count = btv->field_count;
- wakeup->vb.state = STATE_DONE;
+ wakeup->vb.state = VIDEOBUF_DONE;
wake_up(&wakeup->vb.done);
spin_unlock(&btv->s_lock);
}
@@ -4393,7 +3990,7 @@ bttv_irq_switch_video(struct bttv *btv)
}
/* wake up finished buffers */
- bttv_irq_wakeup_video(btv, &old, &new, STATE_DONE);
+ bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_DONE);
spin_unlock(&btv->s_lock);
}
@@ -4426,7 +4023,7 @@ bttv_irq_switch_vbi(struct bttv *btv)
bttv_buffer_activate_vbi(btv, new);
bttv_set_dma(btv, 0);
- bttv_irq_wakeup_vbi(btv, old, STATE_DONE);
+ bttv_irq_wakeup_vbi(btv, old, VIDEOBUF_DONE);
spin_unlock(&btv->s_lock);
}
@@ -4548,8 +4145,9 @@ static irqreturn_t bttv_irq(int irq, void *dev_id)
/* initialitation */
static struct video_device *vdev_init(struct bttv *btv,
- struct video_device *template,
- char *type)
+ const struct video_device *template,
+ const char *type_name,
+ const int type)
{
struct video_device *vfd;
@@ -4560,9 +4158,10 @@ static struct video_device *vdev_init(struct bttv *btv,
vfd->minor = -1;
vfd->dev = &btv->c.pci->dev;
vfd->release = video_device_release;
+ vfd->type = type;
snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
- type, bttv_tvcards[btv->c.type].name);
+ type_name, bttv_tvcards[btv->c.type].name);
return vfd;
}
@@ -4594,6 +4193,11 @@ static void bttv_unregister_video(struct bttv *btv)
/* register video4linux devices */
static int __devinit bttv_register_video(struct bttv *btv)
{
+ int video_type = VID_TYPE_CAPTURE |
+ VID_TYPE_TUNER |
+ VID_TYPE_CLIPPING|
+ VID_TYPE_SCALES;
+
if (no_overlay <= 0) {
bttv_video_template.type |= VID_TYPE_OVERLAY;
} else {
@@ -4601,7 +4205,9 @@ static int __devinit bttv_register_video(struct bttv *btv)
}
/* video */
- btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
+ btv->video_dev = vdev_init(btv, &bttv_video_template,
+ "video", video_type);
+
if (NULL == btv->video_dev)
goto err;
if (video_register_device(btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0)
@@ -4616,7 +4222,9 @@ static int __devinit bttv_register_video(struct bttv *btv)
}
/* vbi */
- btv->vbi_dev = vdev_init(btv, &bttv_vbi_template, "vbi");
+ btv->vbi_dev = vdev_init(btv, &bttv_video_template,
+ "vbi", VID_TYPE_TUNER | VID_TYPE_TELETEXT);
+
if (NULL == btv->vbi_dev)
goto err;
if (video_register_device(btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0)
@@ -4627,7 +4235,8 @@ static int __devinit bttv_register_video(struct bttv *btv)
if (!btv->has_radio)
return 0;
/* radio */
- btv->radio_dev = vdev_init(btv, &radio_template, "radio");
+ btv->radio_dev = vdev_init(btv, &radio_template,
+ "radio", VID_TYPE_TUNER);
if (NULL == btv->radio_dev)
goto err;
if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,radio_nr)<0)
@@ -4768,7 +4377,7 @@ static int __devinit bttv_probe(struct pci_dev *dev,
btv->init.btv = btv;
btv->init.ov.w.width = 320;
btv->init.ov.w.height = 240;
- btv->init.fmt = format_by_palette(VIDEO_PALETTE_RGB24);
+ btv->init.fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
btv->init.width = 320;
btv->init.height = 240;
btv->input = 0;
@@ -5013,14 +4622,17 @@ static int __init bttv_init_module(void)
printk(KERN_WARNING "bttv: bus_register error: %d\n", ret);
return ret;
}
- return pci_register_driver(&bttv_pci_driver);
+ ret = pci_register_driver(&bttv_pci_driver);
+ if (ret < 0)
+ bus_unregister(&bttv_sub_bus_type);
+
+ return ret;
}
static void __exit bttv_cleanup_module(void)
{
pci_unregister_driver(&bttv_pci_driver);
bus_unregister(&bttv_sub_bus_type);
- return;
}
module_init(bttv_init_module);
diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c
index e7c521b8444a..fc9ecb21eec6 100644
--- a/drivers/media/video/bt8xx/bttv-input.c
+++ b/drivers/media/video/bt8xx/bttv-input.c
@@ -69,6 +69,11 @@ static void ir_handle_key(struct bttv *btv)
(ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
ir_input_keydown(ir->dev,&ir->ir,data,data);
} else {
+ /* HACK: Probably, ir->mask_keydown is missing
+ for this board */
+ if (btv->c.type == BTTV_BOARD_WINFAST2000)
+ ir_input_keydown(ir->dev, &ir->ir, data, data);
+
ir_input_nokey(ir->dev,&ir->ir);
}
diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c
index 58986f1a5f1a..e5979f77504c 100644
--- a/drivers/media/video/bt8xx/bttv-risc.c
+++ b/drivers/media/video/bt8xx/bttv-risc.c
@@ -582,7 +582,7 @@ bttv_dma_free(struct videobuf_queue *q,struct bttv *btv, struct bttv_buffer *buf
videobuf_dma_free(dma);
btcx_riscmem_free(btv->c.pci,&buf->bottom);
btcx_riscmem_free(btv->c.pci,&buf->top);
- buf->vb.state = STATE_NEEDS_INIT;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
int
@@ -602,7 +602,7 @@ bttv_buffer_activate_vbi(struct bttv *btv,
if (vbi) {
unsigned int crop, vdelay;
- vbi->vb.state = STATE_ACTIVE;
+ vbi->vb.state = VIDEOBUF_ACTIVE;
list_del(&vbi->vb.queue);
/* VDELAY is start of video, end of VBI capturing. */
@@ -644,12 +644,12 @@ bttv_buffer_activate_video(struct bttv *btv,
/* video capture */
if (NULL != set->top && NULL != set->bottom) {
if (set->top == set->bottom) {
- set->top->vb.state = STATE_ACTIVE;
+ set->top->vb.state = VIDEOBUF_ACTIVE;
if (set->top->vb.queue.next)
list_del(&set->top->vb.queue);
} else {
- set->top->vb.state = STATE_ACTIVE;
- set->bottom->vb.state = STATE_ACTIVE;
+ set->top->vb.state = VIDEOBUF_ACTIVE;
+ set->bottom->vb.state = VIDEOBUF_ACTIVE;
if (set->top->vb.queue.next)
list_del(&set->top->vb.queue);
if (set->bottom->vb.queue.next)
@@ -666,7 +666,7 @@ bttv_buffer_activate_video(struct bttv *btv,
btaor((set->top->btswap & 0x0a) | (set->bottom->btswap & 0x05),
~0x0f, BT848_COLOR_CTL);
} else if (NULL != set->top) {
- set->top->vb.state = STATE_ACTIVE;
+ set->top->vb.state = VIDEOBUF_ACTIVE;
if (set->top->vb.queue.next)
list_del(&set->top->vb.queue);
bttv_apply_geo(btv, &set->top->geo,1);
@@ -677,7 +677,7 @@ bttv_buffer_activate_video(struct bttv *btv,
btaor(set->top->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
btaor(set->top->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL);
} else if (NULL != set->bottom) {
- set->bottom->vb.state = STATE_ACTIVE;
+ set->bottom->vb.state = VIDEOBUF_ACTIVE;
if (set->bottom->vb.queue.next)
list_del(&set->bottom->vb.queue);
bttv_apply_geo(btv, &set->bottom->geo,1);
diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c
index 346ce019bdcb..1f0cc79e2a33 100644
--- a/drivers/media/video/bt8xx/bttv-vbi.c
+++ b/drivers/media/video/bt8xx/bttv-vbi.c
@@ -142,7 +142,7 @@ static int vbi_buffer_prepare(struct videobuf_queue *q,
redo_dma_risc = 1;
}
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
redo_dma_risc = 1;
if (0 != (rc = videobuf_iolock(q, &buf->vb, NULL)))
goto fail;
@@ -189,7 +189,7 @@ static int vbi_buffer_prepare(struct videobuf_queue *q,
/* For bttv_buffer_activate_vbi(). */
buf->geo.vdelay = min_vdelay;
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
buf->vb.field = field;
dprintk("buf prepare %p: top=%p bottom=%p field=%s\n",
vb, &buf->top, &buf->bottom,
@@ -209,7 +209,7 @@ vbi_buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
dprintk("queue %p\n",vb);
- buf->vb.state = STATE_QUEUED;
+ buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue,&btv->vcapture);
if (NULL == btv->cvbi) {
fh->btv->loop_irq |= 4;
@@ -236,10 +236,8 @@ struct videobuf_queue_ops bttv_vbi_qops = {
/* ----------------------------------------------------------------------- */
-static int
-try_fmt (struct v4l2_vbi_format * f,
- const struct bttv_tvnorm * tvnorm,
- __s32 crop_start)
+static int try_fmt(struct v4l2_vbi_format *f, const struct bttv_tvnorm *tvnorm,
+ __s32 crop_start)
{
__s32 min_start, max_start, max_end, f2_offset;
unsigned int i;
@@ -305,10 +303,9 @@ try_fmt (struct v4l2_vbi_format * f,
return 0;
}
-int
-bttv_vbi_try_fmt (struct bttv_fh * fh,
- struct v4l2_vbi_format * f)
+int bttv_try_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
{
+ struct bttv_fh *fh = f;
struct bttv *btv = fh->btv;
const struct bttv_tvnorm *tvnorm;
__s32 crop_start;
@@ -320,13 +317,13 @@ bttv_vbi_try_fmt (struct bttv_fh * fh,
mutex_unlock(&btv->lock);
- return try_fmt(f, tvnorm, crop_start);
+ return try_fmt(&frt->fmt.vbi, tvnorm, crop_start);
}
-int
-bttv_vbi_set_fmt (struct bttv_fh * fh,
- struct v4l2_vbi_format * f)
+
+int bttv_s_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
{
+ struct bttv_fh *fh = f;
struct bttv *btv = fh->btv;
const struct bttv_tvnorm *tvnorm;
__s32 start1, end;
@@ -340,11 +337,12 @@ bttv_vbi_set_fmt (struct bttv_fh * fh,
tvnorm = &bttv_tvnorms[btv->tvnorm];
- rc = try_fmt(f, tvnorm, btv->crop_start);
+ rc = try_fmt(&frt->fmt.vbi, tvnorm, btv->crop_start);
if (0 != rc)
goto fail;
- start1 = f->start[1] - tvnorm->vbistart[1] + tvnorm->vbistart[0];
+ start1 = frt->fmt.vbi.start[1] - tvnorm->vbistart[1] +
+ tvnorm->vbistart[0];
/* First possible line of video capturing. Should be
max(f->start[0] + f->count[0], start1 + f->count[1]) * 2
@@ -352,11 +350,11 @@ bttv_vbi_set_fmt (struct bttv_fh * fh,
pretend the VBI and video capture window may overlap,
so end = start + 1, the lowest possible value, times two
because vbi_fmt.end counts field lines times two. */
- end = max(f->start[0], start1) * 2 + 2;
+ end = max(frt->fmt.vbi.start[0], start1) * 2 + 2;
mutex_lock(&fh->vbi.lock);
- fh->vbi_fmt.fmt = *f;
+ fh->vbi_fmt.fmt = frt->fmt.vbi;
fh->vbi_fmt.tvnorm = tvnorm;
fh->vbi_fmt.end = end;
@@ -370,13 +368,13 @@ bttv_vbi_set_fmt (struct bttv_fh * fh,
return rc;
}
-void
-bttv_vbi_get_fmt (struct bttv_fh * fh,
- struct v4l2_vbi_format * f)
+
+int bttv_g_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
{
+ struct bttv_fh *fh = f;
const struct bttv_tvnorm *tvnorm;
- *f = fh->vbi_fmt.fmt;
+ frt->fmt.vbi = fh->vbi_fmt.fmt;
tvnorm = &bttv_tvnorms[fh->btv->tvnorm];
@@ -391,28 +389,28 @@ bttv_vbi_get_fmt (struct bttv_fh * fh,
max_end = (tvnorm->cropcap.bounds.top
+ tvnorm->cropcap.bounds.height) >> 1;
- f->sampling_rate = tvnorm->Fsc;
+ frt->fmt.vbi.sampling_rate = tvnorm->Fsc;
for (i = 0; i < 2; ++i) {
__s32 new_start;
- new_start = f->start[i]
+ new_start = frt->fmt.vbi.start[i]
+ tvnorm->vbistart[i]
- fh->vbi_fmt.tvnorm->vbistart[i];
- f->start[i] = min(new_start, max_end - 1);
- f->count[i] = min((__s32) f->count[i],
- max_end - f->start[i]);
+ frt->fmt.vbi.start[i] = min(new_start, max_end - 1);
+ frt->fmt.vbi.count[i] =
+ min((__s32) frt->fmt.vbi.count[i],
+ max_end - frt->fmt.vbi.start[i]);
max_end += tvnorm->vbistart[1]
- tvnorm->vbistart[0];
}
}
+ return 0;
}
-void
-bttv_vbi_fmt_reset (struct bttv_vbi_fmt * f,
- int norm)
+void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, int norm)
{
const struct bttv_tvnorm *tvnorm;
unsigned int real_samples_per_line;
diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h
index 19e75d50a107..bf4c339a520c 100644
--- a/drivers/media/video/bt8xx/bttv.h
+++ b/drivers/media/video/bt8xx/bttv.h
@@ -241,7 +241,10 @@ struct tvcard
unsigned int radio_addr;
unsigned int has_radio;
- void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set);
+
+ void (*volume_gpio)(struct bttv *btv, __u16 volume);
+ void (*audio_mode_gpio)(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+
void (*muxsel_hook)(struct bttv *btv, unsigned int input);
};
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
index d4ac4c4b49b4..1305d315cfc5 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -84,6 +84,11 @@
#define clamp(x, low, high) min (max (low, x), high)
+#define BTTV_NORMS (\
+ V4L2_STD_PAL | V4L2_STD_PAL_N | \
+ V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \
+ V4L2_STD_NTSC | V4L2_STD_PAL_M | \
+ V4L2_STD_PAL_60)
/* ---------------------------------------------------------- */
struct bttv_tvnorm {
@@ -112,7 +117,6 @@ extern const struct bttv_tvnorm bttv_tvnorms[];
struct bttv_format {
char *name;
- int palette; /* video4linux 1 */
int fourcc; /* video4linux 2 */
int btformat; /* BT848_COLOR_FMT_* */
int btswap; /* BT848_COLOR_CTL_* */
@@ -253,9 +257,9 @@ int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov,
/* ---------------------------------------------------------- */
/* bttv-vbi.c */
-int bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f);
-void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f);
-int bttv_vbi_set_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f);
+int bttv_try_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_g_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_s_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f);
extern struct videobuf_queue_ops bttv_vbi_qops;
@@ -337,7 +341,9 @@ struct bttv {
/* old gpio interface */
wait_queue_head_t gpioq;
int shutdown;
- void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set);
+
+ void (*volume_gpio)(struct bttv *btv, __u16 volume);
+ void (*audio_mode_gpio)(struct bttv *btv, struct v4l2_tuner *tuner, int set);
/* new gpio interface */
spinlock_t gpio_lock;
@@ -458,10 +464,6 @@ struct bttv {
extern unsigned int bttv_num;
extern struct bttv bttvs[BTTV_MAX];
-/* private ioctls */
-#define BTTV_VERSION _IOR('v' , BASE_VIDIOCPRIVATE+6, int)
-#define BTTV_VBISIZE _IOR('v' , BASE_VIDIOCPRIVATE+8, int)
-
#endif
#define btwrite(dat,adr) writel((dat), btv->bt848_mmio+(adr))
diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c
index 58423525591f..032265383df2 100644
--- a/drivers/media/video/bw-qcam.c
+++ b/drivers/media/video/bw-qcam.c
@@ -82,11 +82,16 @@ OTHER DEALINGS IN THE SOFTWARE.
static unsigned int maxpoll=250; /* Maximum busy-loop count for qcam I/O */
static unsigned int yieldlines=4; /* Yield after this many during capture */
static int video_nr = -1;
+static unsigned int force_init; /* Whether to probe aggressively */
module_param(maxpoll, int, 0);
module_param(yieldlines, int, 0);
module_param(video_nr, int, 0);
+/* Set force_init=1 to avoid detection by polling status register and
+ * immediately attempt to initialize qcam */
+module_param(force_init, int, 0);
+
static inline int read_lpstatus(struct qcam_device *q)
{
return parport_read_status(q->pport);
@@ -331,6 +336,9 @@ static int qc_detect(struct qcam_device *q)
int count = 0;
int i;
+ if (force_init)
+ return 1;
+
lastreg = reg = read_lpstatus(q) & 0xf0;
for (i = 0; i < 500; i++)
@@ -354,12 +362,12 @@ static int qc_detect(struct qcam_device *q)
/* Be (even more) liberal in what you accept... */
-/* if (count > 30 && count < 200) */
if (count > 20 && count < 400) {
return 1; /* found */
} else {
printk(KERN_ERR "No Quickcam found on port %s\n",
q->pport->name);
+ printk(KERN_DEBUG "Quickcam detection counter: %u\n", count);
return 0; /* not found */
}
}
diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c
new file mode 100644
index 000000000000..fae469ce16f5
--- /dev/null
+++ b/drivers/media/video/cs5345.c
@@ -0,0 +1,168 @@
+/*
+ * cs5345 Cirrus Logic 24-bit, 192 kHz Stereo Audio ADC
+ * Copyright (C) 2007 Hans Verkuil
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-i2c-drv.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+
+MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC");
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static int debug;
+
+module_param(debug, bool, 0644);
+
+MODULE_PARM_DESC(debug, "Debugging messages\n\t\t\t0=Off (default), 1=On");
+
+
+/* ----------------------------------------------------------------------- */
+
+static inline int cs5345_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int cs5345_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ struct v4l2_routing *route = arg;
+ struct v4l2_control *ctrl = arg;
+
+ switch (cmd) {
+ case VIDIOC_INT_G_AUDIO_ROUTING:
+ route->input = cs5345_read(client, 0x09) & 7;
+ route->input |= cs5345_read(client, 0x05) & 0x70;
+ route->output = 0;
+ break;
+
+ case VIDIOC_INT_S_AUDIO_ROUTING:
+ if ((route->input & 0xf) > 6) {
+ v4l_err(client, "Invalid input %d.\n", route->input);
+ return -EINVAL;
+ }
+ cs5345_write(client, 0x09, route->input & 0xf);
+ cs5345_write(client, 0x05, route->input & 0xf0);
+ break;
+
+ case VIDIOC_G_CTRL:
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl->value = (cs5345_read(client, 0x04) & 0x08) != 0;
+ break;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ ctrl->value = cs5345_read(client, 0x07) & 0x3f;
+ if (ctrl->value >= 32)
+ ctrl->value = ctrl->value - 64;
+ break;
+
+ case VIDIOC_S_CTRL:
+ break;
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ cs5345_write(client, 0x04, ctrl->value ? 0x80 : 0);
+ break;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ if (ctrl->value > 24 || ctrl->value < -24)
+ return -EINVAL;
+ cs5345_write(client, 0x07, ((u8)ctrl->value) & 0x3f);
+ cs5345_write(client, 0x08, ((u8)ctrl->value) & 0x3f);
+ break;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_G_REGISTER:
+ case VIDIOC_DBG_S_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (cmd == VIDIOC_DBG_G_REGISTER)
+ reg->val = cs5345_read(client, reg->reg & 0x1f);
+ else
+ cs5345_write(client, reg->reg & 0x1f, reg->val & 0x1f);
+ break;
+ }
+#endif
+
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client,
+ arg, V4L2_IDENT_CS5345, 0);
+
+ case VIDIOC_LOG_STATUS:
+ {
+ u8 v = cs5345_read(client, 0x09) & 7;
+ u8 m = cs5345_read(client, 0x04);
+ int vol = cs5345_read(client, 0x08) & 0x3f;
+
+ v4l_info(client, "Input: %d%s\n", v,
+ (m & 0x80) ? " (muted)" : "");
+ if (vol >= 32)
+ vol = vol - 64;
+ v4l_info(client, "Volume: %d dB\n", vol);
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int cs5345_probe(struct i2c_client *client)
+{
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ cs5345_write(client, 0x02, 0x00);
+ cs5345_write(client, 0x04, 0x01);
+ cs5345_write(client, 0x09, 0x01);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "cs5345",
+ .driverid = I2C_DRIVERID_CS5345,
+ .command = cs5345_command,
+ .probe = cs5345_probe,
+};
+
diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c
index a73e285af730..f41bfde045fe 100644
--- a/drivers/media/video/cs53l32a.c
+++ b/drivers/media/video/cs53l32a.c
@@ -29,12 +29,13 @@
#include <linux/videodev.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv-legacy.h>
MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
MODULE_AUTHOR("Martin Vaughan");
MODULE_LICENSE("GPL");
-static int debug = 0;
+static int debug;
module_param(debug, bool, 0644);
@@ -57,8 +58,7 @@ static int cs53l32a_read(struct i2c_client *client, u8 reg)
return i2c_smbus_read_byte_data(client, reg);
}
-static int cs53l32a_command(struct i2c_client *client, unsigned int cmd,
- void *arg)
+static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg)
{
struct v4l2_routing *route = arg;
struct v4l2_control *ctrl = arg;
@@ -105,7 +105,8 @@ static int cs53l32a_command(struct i2c_client *client, unsigned int cmd,
break;
case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_CS53l32A, 0);
+ return v4l2_chip_ident_i2c_client(client,
+ arg, V4L2_IDENT_CS53l32A, 0);
case VIDIOC_LOG_STATUS:
{
@@ -134,27 +135,18 @@ static int cs53l32a_command(struct i2c_client *client, unsigned int cmd,
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
-static struct i2c_driver i2c_driver;
-
-static int cs53l32a_attach(struct i2c_adapter *adapter, int address, int kind)
+static int cs53l32a_probe(struct i2c_client *client)
{
- struct i2c_client *client;
int i;
/* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return 0;
-
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == 0)
- return -ENOMEM;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver;
snprintf(client->name, sizeof(client->name) - 1, "cs53l32a");
- v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name);
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
for (i = 1; i <= 7; i++) {
u8 v = cs53l32a_read(client, i);
@@ -179,55 +171,13 @@ static int cs53l32a_attach(struct i2c_adapter *adapter, int address, int kind)
v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v);
}
-
- i2c_attach_client(client);
-
return 0;
}
-static int cs53l32a_probe(struct i2c_adapter *adapter)
-{
- if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, cs53l32a_attach);
- return 0;
-}
-
-static int cs53l32a_detach(struct i2c_client *client)
-{
- int err;
-
- err = i2c_detach_client(client);
- if (err) {
- return err;
- }
- kfree(client);
-
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-/* i2c implementation */
-static struct i2c_driver i2c_driver = {
- .driver = {
- .name = "cs53l32a",
- },
- .id = I2C_DRIVERID_CS53L32A,
- .attach_adapter = cs53l32a_probe,
- .detach_client = cs53l32a_detach,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "cs53l32a",
+ .driverid = I2C_DRIVERID_CS53L32A,
.command = cs53l32a_command,
+ .probe = cs53l32a_probe,
};
-
-static int __init cs53l32a_init_module(void)
-{
- return i2c_add_driver(&i2c_driver);
-}
-
-static void __exit cs53l32a_cleanup_module(void)
-{
- i2c_del_driver(&i2c_driver);
-}
-
-module_init(cs53l32a_init_module);
-module_exit(cs53l32a_cleanup_module);
diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c
index 62304255dcae..c592899a2317 100644
--- a/drivers/media/video/cx2341x.c
+++ b/drivers/media/video/cx2341x.c
@@ -34,7 +34,7 @@ MODULE_DESCRIPTION("cx23415/6 driver");
MODULE_AUTHOR("Hans Verkuil");
MODULE_LICENSE("GPL");
-static int debug = 0;
+static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
@@ -75,6 +75,7 @@ const u32 cx2341x_mpeg_ctrls[] = {
V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
0
};
+EXPORT_SYMBOL(cx2341x_mpeg_ctrls);
/* Map the control ID to the correct field in the cx2341x_mpeg_params
@@ -281,13 +282,14 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy,
return -EBUSY;
params->stream_type = ctrl->value;
params->video_encoding =
- (params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
- params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
- V4L2_MPEG_VIDEO_ENCODING_MPEG_1 : V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
- if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) {
+ (params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
+ params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1 :
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+ if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
/* MPEG-1 implies CBR */
- params->video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
- }
+ params->video_bitrate_mode =
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
break;
case V4L2_CID_MPEG_STREAM_VBI_FMT:
params->stream_vbi_fmt = ctrl->value;
@@ -334,7 +336,8 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy,
return 0;
}
-static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
+static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl,
+ s32 min, s32 max, s32 step, s32 def)
{
const char *name;
@@ -417,7 +420,8 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 ma
return 0;
}
-int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl *qctrl)
+int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params,
+ struct v4l2_queryctrl *qctrl)
{
int err;
@@ -440,7 +444,8 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl
case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
err = v4l2_ctrl_query_fill_std(qctrl);
- if (err == 0 && params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO)
+ if (err == 0 &&
+ params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO)
qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return err;
@@ -455,13 +460,16 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
err = v4l2_ctrl_query_fill_std(qctrl);
- if (err == 0 && params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ if (err == 0 &&
+ params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return err;
case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
err = v4l2_ctrl_query_fill_std(qctrl);
- if (err == 0 && params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+ if (err == 0 &&
+ params->video_bitrate_mode ==
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return err;
@@ -476,80 +484,90 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl
/* CX23415/6 specific */
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
return cx2341x_ctrl_query_fill(qctrl,
- V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
- V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1,
- V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL);
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL);
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
cx2341x_ctrl_query_fill(qctrl, 0, 15, 1, 0);
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- if (params->video_spatial_filter_mode == V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (params->video_spatial_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
cx2341x_ctrl_query_fill(qctrl,
- V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
- V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, 1,
- V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF);
- if (params->video_spatial_filter_mode == V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
+ 1,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF);
+ if (params->video_spatial_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
cx2341x_ctrl_query_fill(qctrl,
- V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
- V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, 1,
- V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF);
- if (params->video_spatial_filter_mode == V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ 1,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF);
+ if (params->video_spatial_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
return cx2341x_ctrl_query_fill(qctrl,
- V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
- V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1,
- V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL);
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL);
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
cx2341x_ctrl_query_fill(qctrl, 0, 31, 1, 0);
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- if (params->video_temporal_filter_mode == V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (params->video_temporal_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
return cx2341x_ctrl_query_fill(qctrl,
- V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
- V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1,
- V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF);
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF);
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 255);
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- if (params->video_median_filter_type == V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 0);
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- if (params->video_median_filter_type == V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 255);
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- if (params->video_median_filter_type == V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 0);
qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- if (params->video_median_filter_type == V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
@@ -560,6 +578,7 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl
}
}
+EXPORT_SYMBOL(cx2341x_ctrl_query);
const char **cx2341x_ctrl_get_menu(u32 id)
{
@@ -629,6 +648,7 @@ const char **cx2341x_ctrl_get_menu(u32 id)
return v4l2_ctrl_get_menu(id);
}
}
+EXPORT_SYMBOL(cx2341x_ctrl_get_menu);
static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
{
@@ -637,9 +657,8 @@ static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
((1 + params->audio_l2_bitrate) << 4) |
(params->audio_mode << 8) |
(params->audio_mode_extension << 10) |
- (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) ?
- 3 :
- params->audio_emphasis) << 12) |
+ (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17)
+ ? 3 : params->audio_emphasis) << 12) |
(params->audio_crc << 14);
}
@@ -679,19 +698,19 @@ int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy,
if (err)
break;
}
- if (err == 0 && params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
- params->video_bitrate_peak < params->video_bitrate) {
+ if (err == 0 &&
+ params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ params->video_bitrate_peak < params->video_bitrate) {
err = -ERANGE;
ctrls->error_idx = ctrls->count;
}
- if (err) {
+ if (err)
ctrls->error_idx = i;
- }
- else {
+ else
cx2341x_calc_audio_properties(params);
- }
return err;
}
+EXPORT_SYMBOL(cx2341x_ext_ctrls);
void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
{
@@ -732,13 +751,18 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
.video_mute_yuv = 0x008080, /* YCbCr value for black */
/* encoding filters */
- .video_spatial_filter_mode = V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ .video_spatial_filter_mode =
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
.video_spatial_filter = 0,
- .video_luma_spatial_filter_type = V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR,
- .video_chroma_spatial_filter_type = V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
- .video_temporal_filter_mode = V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ .video_luma_spatial_filter_type =
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ .video_chroma_spatial_filter_type =
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ .video_temporal_filter_mode =
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
.video_temporal_filter = 8,
- .video_median_filter_type = V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ .video_median_filter_type =
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
.video_luma_median_filter_top = 255,
.video_luma_median_filter_bottom = 0,
.video_chroma_median_filter_top = 255,
@@ -748,8 +772,10 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
*p = default_params;
cx2341x_calc_audio_properties(p);
}
+EXPORT_SYMBOL(cx2341x_fill_defaults);
-static int cx2341x_api(void *priv, cx2341x_mbox_func func, int cmd, int args, ...)
+static int cx2341x_api(void *priv, cx2341x_mbox_func func,
+ u32 cmd, int args, ...)
{
u32 data[CX2341X_MBOX_MAX_DATA];
va_list vargs;
@@ -757,15 +783,17 @@ static int cx2341x_api(void *priv, cx2341x_mbox_func func, int cmd, int args, ..
va_start(vargs, args);
- for (i = 0; i < args; i++) {
+ for (i = 0; i < args; i++)
data[i] = va_arg(vargs, int);
- }
va_end(vargs);
return func(priv, cmd, args, 0, data);
}
+#define NEQ(field) (old->field != new->field)
+
int cx2341x_update(void *priv, cx2341x_mbox_func func,
- const struct cx2341x_mpeg_params *old, const struct cx2341x_mpeg_params *new)
+ const struct cx2341x_mpeg_params *old,
+ const struct cx2341x_mpeg_params *new)
{
static int mpeg_stream_type[] = {
0, /* MPEG-2 PS */
@@ -777,17 +805,18 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
};
int err = 0;
+ int force = (old == NULL);
u16 temporal = new->video_temporal_filter;
cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0);
- if (old == NULL || old->is_50hz != new->is_50hz) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1, new->is_50hz);
+ if (force || NEQ(is_50hz)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1,
+ new->is_50hz);
if (err) return err;
}
- if (old == NULL || old->width != new->width || old->height != new->height ||
- old->video_encoding != new->video_encoding) {
+ if (force || NEQ(width) || NEQ(height) || NEQ(video_encoding)) {
u16 w = new->width;
u16 h = new->height;
@@ -795,69 +824,74 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
w /= 2;
h /= 2;
}
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w);
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2,
+ h, w);
if (err) return err;
}
if (new->width != 720 || new->height != (new->is_50hz ? 576 : 480)) {
- /* Adjust temporal filter if necessary. The problem with the temporal
- filter is that it works well with full resolution capturing, but
- not when the capture window is scaled (the filter introduces
- a ghosting effect). So if the capture window is scaled, then
- force the filter to 0.
+ /* Adjust temporal filter if necessary. The problem with the
+ temporal filter is that it works well with full resolution
+ capturing, but not when the capture window is scaled (the
+ filter introduces a ghosting effect). So if the capture
+ window is scaled, then force the filter to 0.
For full resolution the filter really improves the video
- quality, especially if the original video quality is suboptimal. */
+ quality, especially if the original video quality is
+ suboptimal. */
temporal = 0;
}
- if (old == NULL || old->stream_type != new->stream_type) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[new->stream_type]);
+ if (force || NEQ(stream_type)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1,
+ mpeg_stream_type[new->stream_type]);
if (err) return err;
}
- if (old == NULL || old->video_aspect != new->video_aspect) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1, 1 + new->video_aspect);
+ if (force || NEQ(video_aspect)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1,
+ 1 + new->video_aspect);
if (err) return err;
}
- if (old == NULL || old->video_b_frames != new->video_b_frames ||
- old->video_gop_size != new->video_gop_size) {
+ if (force || NEQ(video_b_frames) || NEQ(video_gop_size)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
new->video_gop_size, new->video_b_frames + 1);
if (err) return err;
}
- if (old == NULL || old->video_gop_closure != new->video_gop_closure) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1, new->video_gop_closure);
+ if (force || NEQ(video_gop_closure)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1,
+ new->video_gop_closure);
if (err) return err;
}
- if (old == NULL || old->audio_properties != new->audio_properties) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new->audio_properties);
+ if (force || NEQ(audio_properties)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES,
+ 1, new->audio_properties);
if (err) return err;
}
- if (old == NULL || old->audio_mute != new->audio_mute) {
- err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, new->audio_mute);
+ if (force || NEQ(audio_mute)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1,
+ new->audio_mute);
if (err) return err;
}
- if (old == NULL || old->video_bitrate_mode != new->video_bitrate_mode ||
- old->video_bitrate != new->video_bitrate ||
- old->video_bitrate_peak != new->video_bitrate_peak) {
+ if (force || NEQ(video_bitrate_mode) || NEQ(video_bitrate) ||
+ NEQ(video_bitrate_peak)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_BIT_RATE, 5,
new->video_bitrate_mode, new->video_bitrate,
new->video_bitrate_peak / 400, 0, 0);
if (err) return err;
}
- if (old == NULL || old->video_spatial_filter_mode != new->video_spatial_filter_mode ||
- old->video_temporal_filter_mode != new->video_temporal_filter_mode ||
- old->video_median_filter_type != new->video_median_filter_type) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE, 2,
- new->video_spatial_filter_mode | (new->video_temporal_filter_mode << 1),
+ if (force || NEQ(video_spatial_filter_mode) ||
+ NEQ(video_temporal_filter_mode) ||
+ NEQ(video_median_filter_type)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE,
+ 2, new->video_spatial_filter_mode |
+ (new->video_temporal_filter_mode << 1),
new->video_median_filter_type);
if (err) return err;
}
- if (old == NULL ||
- old->video_luma_median_filter_bottom != new->video_luma_median_filter_bottom ||
- old->video_luma_median_filter_top != new->video_luma_median_filter_top ||
- old->video_chroma_median_filter_bottom != new->video_chroma_median_filter_bottom ||
- old->video_chroma_median_filter_top != new->video_chroma_median_filter_top) {
+ if (force || NEQ(video_luma_median_filter_bottom) ||
+ NEQ(video_luma_median_filter_top) ||
+ NEQ(video_chroma_median_filter_bottom) ||
+ NEQ(video_chroma_median_filter_top)) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_CORING_LEVELS, 4,
new->video_luma_median_filter_bottom,
new->video_luma_median_filter_top,
@@ -865,36 +899,39 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
new->video_chroma_median_filter_top);
if (err) return err;
}
- if (old == NULL ||
- old->video_luma_spatial_filter_type != new->video_luma_spatial_filter_type ||
- old->video_chroma_spatial_filter_type != new->video_chroma_spatial_filter_type) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2,
- new->video_luma_spatial_filter_type, new->video_chroma_spatial_filter_type);
+ if (force || NEQ(video_luma_spatial_filter_type) ||
+ NEQ(video_chroma_spatial_filter_type)) {
+ err = cx2341x_api(priv, func,
+ CX2341X_ENC_SET_SPATIAL_FILTER_TYPE,
+ 2, new->video_luma_spatial_filter_type,
+ new->video_chroma_spatial_filter_type);
if (err) return err;
}
- if (old == NULL ||
- old->video_spatial_filter != new->video_spatial_filter ||
- old->video_temporal_filter != temporal) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2,
- new->video_spatial_filter, temporal);
+ if (force || NEQ(video_spatial_filter) ||
+ old->video_temporal_filter != temporal) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS,
+ 2, new->video_spatial_filter, temporal);
if (err) return err;
}
- if (old == NULL || old->video_temporal_decimation != new->video_temporal_decimation) {
- err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE, 1,
- new->video_temporal_decimation);
+ if (force || NEQ(video_temporal_decimation)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE,
+ 1, new->video_temporal_decimation);
if (err) return err;
}
- if (old == NULL || old->video_mute != new->video_mute ||
- (new->video_mute && old->video_mute_yuv != new->video_mute_yuv)) {
- err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, new->video_mute | (new->video_mute_yuv << 8));
+ if (force || NEQ(video_mute) ||
+ (new->video_mute && NEQ(video_mute_yuv))) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1,
+ new->video_mute | (new->video_mute_yuv << 8));
if (err) return err;
}
- if (old == NULL || old->stream_insert_nav_packets != new->stream_insert_nav_packets) {
- err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, 7, new->stream_insert_nav_packets);
+ if (force || NEQ(stream_insert_nav_packets)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2,
+ 7, new->stream_insert_nav_packets);
if (err) return err;
}
return 0;
}
+EXPORT_SYMBOL(cx2341x_update);
static const char *cx2341x_menu_item(struct cx2341x_mpeg_params *p, u32 id)
{
@@ -943,18 +980,17 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix)
cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT),
cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE),
p->video_bitrate);
- if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) {
+ if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
printk(", Peak %d", p->video_bitrate_peak);
- }
printk("\n");
- printk(KERN_INFO "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n",
+ printk(KERN_INFO
+ "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n",
prefix,
p->video_gop_size, p->video_b_frames,
p->video_gop_closure ? "" : "No ");
- if (p->video_temporal_decimation) {
+ if (p->video_temporal_decimation)
printk(KERN_INFO "%s: Video: Temporal Decimation %d\n",
prefix, p->video_temporal_decimation);
- }
/* Audio */
printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s",
@@ -964,10 +1000,9 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix)
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE),
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE),
p->audio_mute ? " (muted)" : "");
- if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) {
- printk(", %s",
- cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE_EXTENSION));
- }
+ if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO)
+ printk(", %s", cx2341x_menu_item(p,
+ V4L2_CID_MPEG_AUDIO_MODE_EXTENSION));
printk(", %s, %s\n",
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_EMPHASIS),
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC));
@@ -975,33 +1010,33 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix)
/* Encoding filters */
printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n",
prefix,
- cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE),
- cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE),
- cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE),
p->video_spatial_filter);
- if (p->width != 720 || p->height != (p->is_50hz ? 576 : 480)) {
+
+ if (p->width != 720 || p->height != (p->is_50hz ? 576 : 480))
temporal = 0;
- }
+
printk(KERN_INFO "%s: Temporal Filter: %s, %d\n",
prefix,
- cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE),
temporal);
- printk(KERN_INFO "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n",
+ printk(KERN_INFO
+ "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n",
prefix,
- cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE),
p->video_luma_median_filter_bottom,
p->video_luma_median_filter_top,
p->video_chroma_median_filter_bottom,
p->video_chroma_median_filter_top);
}
-
-EXPORT_SYMBOL(cx2341x_fill_defaults);
-EXPORT_SYMBOL(cx2341x_ctrl_query);
-EXPORT_SYMBOL(cx2341x_ctrl_get_menu);
-EXPORT_SYMBOL(cx2341x_ext_ctrls);
-EXPORT_SYMBOL(cx2341x_update);
EXPORT_SYMBOL(cx2341x_log_status);
-EXPORT_SYMBOL(cx2341x_mpeg_ctrls);
/*
* Local variables:
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index 081ee6e1536f..1fd326fe4113 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -12,6 +12,10 @@ config VIDEO_CX23885
select DVB_S5H1409 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_PLL if !DVB_FE_CUSTOMISE
+ select TUNER_XC2028 if !DVB_FE_CUSTOMIZE
+ select TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
+ select DVB_TDA18271 if !DVB_FE_CUSTOMIZE
+ select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
---help---
This is a video4linux driver for Conexant 23885 based
TV cards.
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
index 665067022d2a..32c90be50602 100644
--- a/drivers/media/video/cx23885/Makefile
+++ b/drivers/media/video/cx23885/Makefile
@@ -1,4 +1,4 @@
-cx23885-objs := cx23885-cards.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o
+cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index b9012acabb2f..2d414dad5c31 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -23,6 +23,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
+#include <media/cx25840.h>
#include "cx23885.h"
@@ -32,6 +33,8 @@
struct cx23885_board cx23885_boards[] = {
[CX23885_BOARD_UNKNOWN] = {
.name = "UNKNOWN/GENERIC",
+ /* Ensure safe default for unknown boards */
+ .clk_freq = 0,
.input = {{
.type = CX23885_VMUX_COMPOSITE1,
.vmux = 0,
@@ -69,23 +72,29 @@ struct cx23885_board cx23885_boards[] = {
},
[CX23885_BOARD_HAUPPAUGE_HVR1800] = {
.name = "Hauppauge WinTV-HVR1800",
+ .porta = CX23885_ANALOG_VIDEO,
.portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .tuner_addr = 0x42, /* 0x84 >> 1 */
.input = {{
.type = CX23885_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0xff00,
- },{
- .type = CX23885_VMUX_DEBUG,
- .vmux = 0,
- .gpio0 = 0xff01,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1,
+ .gpio0 = 0,
},{
.type = CX23885_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0xff02,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .gpio0 = 0,
},{
.type = CX23885_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0xff02,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .gpio0 = 0,
}},
},
[CX23885_BOARD_HAUPPAUGE_HVR1250] = {
@@ -113,6 +122,14 @@ struct cx23885_board cx23885_boards[] = {
.name = "DViCO FusionHDTV5 Express",
.portb = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_HAUPPAUGE_HVR1500Q] = {
+ .name = "Hauppauge WinTV-HVR1500Q",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1500] = {
+ .name = "Hauppauge WinTV-HVR1500",
+ .portc = CX23885_MPEG_DVB,
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -138,12 +155,32 @@ struct cx23885_subid cx23885_subids[] = {
.card = CX23885_BOARD_HAUPPAUGE_HVR1800,
},{
.subvendor = 0x0070,
+ .subdevice = 0x7809,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1800,
+ },{
+ .subvendor = 0x0070,
.subdevice = 0x7911,
.card = CX23885_BOARD_HAUPPAUGE_HVR1250,
},{
.subvendor = 0x18ac,
.subdevice = 0xd500,
.card = CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x7790,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500Q,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x7797,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500Q,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x7710,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x7717,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -184,9 +221,19 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
switch (tv.model)
{
case 76601: /* WinTV-HVR1800lp (PCIe, Retail, No IR, Dual channel ATSC and MPEG2 HW Encoder */
- case 77001: /* WinTV-HVR1500 (Express Card, Retail, No IR, ATSC and Basic analog */
- case 78501: /* WinTV-HVR1800 (PCIe, Retail, IR, Dual channel ATSC and MPEG2 HW Encoder */
- case 78521: /* WinTV-HVR1800 (PCIe, Retail, 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 */
+ case 77041: /* WinTV-HVR1500Q (Express Card, OEM, No IR, ATSC/QAM and Basic analog */
+ case 77051: /* WinTV-HVR1500Q (Express Card, Retail, No IR, ATSC/QAM and Basic analog */
+ case 78011: /* WinTV-HVR1800 (PCIe, Retail, 3.5mm in, IR, No FM, Dual channel ATSC and MPEG2 HW Encoder */
+ case 78501: /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, FM, Dual channel ATSC and MPEG2 HW Encoder */
+ case 78521: /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, FM, Dual channel ATSC and MPEG2 HW Encoder */
+ case 78531: /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, No FM, Dual channel ATSC and MPEG2 HW Encoder */
+ case 78631: /* WinTV-HVR1800 (PCIe, OEM, No IR, No FM, Dual channel ATSC and MPEG2 HW Encoder */
+ case 79001: /* WinTV-HVR1250 (PCIe, Retail, IR, full height, ATSC and Basic analog */
+ case 79101: /* WinTV-HVR1250 (PCIe, Retail, IR, half height, ATSC and Basic analog */
+ case 79571: /* WinTV-HVR1250 (PCIe, OEM, No IR, full height, ATSC and Basic analog */
+ case 79671: /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ATSC and Basic analog */
break;
default:
printk("%s: warning: unknown hauppauge model #%d\n", dev->name, tv.model);
@@ -197,6 +244,34 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
dev->name, tv.model);
}
+/* Tuner callback function for cx23885 boards. Currently only needed
+ * for HVR1500Q, which has an xc5000 tuner.
+ */
+int cx23885_tuner_callback(void *priv, int command, int arg)
+{
+ struct cx23885_i2c *bus = priv;
+ struct cx23885_dev *dev = bus->dev;
+
+ switch(dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ if(command == 0) { /* Tuner Reset Command from xc5000 */
+ /* Drive the tuner into reset and out */
+ cx_clear(GP0_IO, 0x00000004);
+ mdelay(200);
+ cx_set(GP0_IO, 0x00000004);
+ return 0;
+ }
+ else {
+ printk(KERN_ERR
+ "%s(): Unknow command.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ return 0; /* Should never be here */
+}
+
void cx23885_gpio_setup(struct cx23885_dev *dev)
{
switch(dev->board) {
@@ -204,6 +279,23 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
/* GPIO-0 cx24227 demodulator reset */
cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ /* GPIO-0 cx24227 demodulator */
+ /* GPIO-2 xc3028 tuner */
+
+ /* Put the parts into reset */
+ cx_set(GP0_IO, 0x00050000);
+ cx_clear(GP0_IO, 0x00000005);
+ msleep(5);
+
+ /* Bring the parts out of reset */
+ cx_set(GP0_IO, 0x00050005);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ /* GPIO-0 cx24227 demodulator reset */
+ /* GPIO-2 xc5000 tuner reset */
+ cx_set(GP0_IO, 0x00050005); /* Bring the part out of reset */
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1800:
/* GPIO-0 656_CLK */
/* GPIO-1 656_D0 */
@@ -212,7 +304,14 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
/* GPIO-11-14 cx23417 addr0-3 */
/* GPIO-15-18 cx23417 READY, CS, RD, WR */
/* GPIO-19 IR_RX */
- // FIXME: Analog requires the tuner is brought out of reset
+
+ /* Force the TDA8295A into reset and back */
+ cx_set(GP0_IO, 0x00040004);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x00000004);
+ mdelay(20);
+ cx_set(GP0_IO, 0x00040004);
+ mdelay(20);
break;
}
}
@@ -221,6 +320,8 @@ int cx23885_ir_init(struct cx23885_dev *dev)
{
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1800:
/* FIXME: Implement me */
break;
@@ -244,6 +345,8 @@ void cx23885_card_setup(struct cx23885_dev *dev)
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1800:
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
if (dev->i2c_bus[0].i2c_rc == 0)
@@ -258,6 +361,8 @@ void cx23885_card_setup(struct cx23885_dev *dev)
ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
break;
case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1800:
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
default:
@@ -270,8 +375,6 @@ void cx23885_card_setup(struct cx23885_dev *dev)
/* ------------------------------------------------------------------ */
-EXPORT_SYMBOL(cx23885_boards);
-
/*
* Local variables:
* c-basic-offset: 8
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index 3cdd136477e5..8e40c7bcc06d 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -36,7 +36,7 @@ MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>");
MODULE_LICENSE("GPL");
-static unsigned int debug = 0;
+static unsigned int debug;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debug messages");
@@ -44,13 +44,15 @@ static unsigned int card[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
module_param_array(card, int, NULL, 0444);
MODULE_PARM_DESC(card,"card type");
-#define dprintk(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s/0: " fmt, dev->name , ## arg)
+#define dprintk(level, fmt, arg...)\
+ do { if (debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
static unsigned int cx23885_devcount;
static DEFINE_MUTEX(devlist);
-static LIST_HEAD(cx23885_devlist);
+LIST_HEAD(cx23885_devlist);
#define NO_SYNC_LINE (-1U)
@@ -73,14 +75,14 @@ static LIST_HEAD(cx23885_devlist);
* 0x00010ea0 0x00010xxx Free
*/
-struct sram_channel cx23885_sram_channels[] = {
+static struct sram_channel cx23885_sram_channels[] = {
[SRAM_CH01] = {
- .name = "test ch1",
+ .name = "VID A",
.cmds_start = 0x10000,
- .ctrl_start = 0x10500,
- .cdt = 0x10900,
- .fifo_start = 0x3000,
- .fifo_size = 0x1000,
+ .ctrl_start = 0x105b0,
+ .cdt = 0x107b0,
+ .fifo_start = 0x40,
+ .fifo_size = 0x2800,
.ptr1_reg = DMA1_PTR1,
.ptr2_reg = DMA1_PTR2,
.cnt1_reg = DMA1_CNT1,
@@ -102,8 +104,8 @@ struct sram_channel cx23885_sram_channels[] = {
[SRAM_CH03] = {
.name = "TS1 B",
.cmds_start = 0x100A0,
- .ctrl_start = 0x10780,
- .cdt = 0x10400,
+ .ctrl_start = 0x10630,
+ .cdt = 0x10870,
.fifo_start = 0x5000,
.fifo_size = 0x1000,
.ptr1_reg = DMA3_PTR1,
@@ -139,7 +141,7 @@ struct sram_channel cx23885_sram_channels[] = {
.name = "TS2 C",
.cmds_start = 0x10140,
.ctrl_start = 0x10680,
- .cdt = 0x10480,
+ .cdt = 0x108d0,
.fifo_start = 0x6000,
.fifo_size = 0x1000,
.ptr1_reg = DMA5_PTR1,
@@ -205,14 +207,14 @@ struct sram_channel cx23885_sram_channels[] = {
* 0x00010ea0 0x00010xxx Free
*/
-struct sram_channel cx23887_sram_channels[] = {
+static struct sram_channel cx23887_sram_channels[] = {
[SRAM_CH01] = {
- .name = "test ch1",
- .cmds_start = 0x0,
- .ctrl_start = 0x0,
- .cdt = 0x0,
- .fifo_start = 0x0,
- .fifo_size = 0x0,
+ .name = "VID A",
+ .cmds_start = 0x10000,
+ .ctrl_start = 0x105b0,
+ .cdt = 0x107b0,
+ .fifo_start = 0x40,
+ .fifo_size = 0x2800,
.ptr1_reg = DMA1_PTR1,
.ptr2_reg = DMA1_PTR2,
.cnt1_reg = DMA1_CNT1,
@@ -231,12 +233,12 @@ struct sram_channel cx23887_sram_channels[] = {
.cnt2_reg = DMA2_CNT2,
},
[SRAM_CH03] = {
- .name = "ch3",
- .cmds_start = 0x0,
- .ctrl_start = 0x0,
- .cdt = 0x0,
- .fifo_start = 0x0,
- .fifo_size = 0x0,
+ .name = "TS1 B",
+ .cmds_start = 0x100A0,
+ .ctrl_start = 0x10780,
+ .cdt = 0x10400,
+ .fifo_start = 0x5000,
+ .fifo_size = 0x1000,
.ptr1_reg = DMA3_PTR1,
.ptr2_reg = DMA3_PTR2,
.cnt1_reg = DMA3_CNT1,
@@ -357,7 +359,7 @@ static int cx23885_risc_decode(u32 risc)
}
void cx23885_wakeup(struct cx23885_tsport *port,
- struct cx23885_dmaqueue *q, u32 count)
+ struct cx23885_dmaqueue *q, u32 count)
{
struct cx23885_dev *dev = port->dev;
struct cx23885_buffer *buf;
@@ -378,7 +380,7 @@ void cx23885_wakeup(struct cx23885_tsport *port,
do_gettimeofday(&buf->vb.ts);
dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i,
count, buf->count);
- buf->vb.state = STATE_DONE;
+ buf->vb.state = VIDEOBUF_DONE;
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
}
@@ -391,12 +393,10 @@ void cx23885_wakeup(struct cx23885_tsport *port,
printk("%s: %d buffers handled (should be 1)\n",
__FUNCTION__, bc);
}
-void cx23885_sram_channel_dump(struct cx23885_dev *dev,
- struct sram_channel *ch);
int cx23885_sram_channel_setup(struct cx23885_dev *dev,
- struct sram_channel *ch,
- unsigned int bpl, u32 risc)
+ struct sram_channel *ch,
+ unsigned int bpl, u32 risc)
{
unsigned int i, lines;
u32 cdt;
@@ -468,7 +468,7 @@ int cx23885_sram_channel_setup(struct cx23885_dev *dev,
}
void cx23885_sram_channel_dump(struct cx23885_dev *dev,
- struct sram_channel *ch)
+ struct sram_channel *ch)
{
static char *name[] = {
"init risc lo",
@@ -529,8 +529,8 @@ void cx23885_sram_channel_dump(struct cx23885_dev *dev,
dev->name, cx_read(ch->cnt2_reg));
}
-void cx23885_risc_disasm(struct cx23885_tsport *port,
- struct btcx_riscmem *risc)
+static void cx23885_risc_disasm(struct cx23885_tsport *port,
+ struct btcx_riscmem *risc)
{
struct cx23885_dev *dev = port->dev;
unsigned int i, j, n;
@@ -548,7 +548,7 @@ void cx23885_risc_disasm(struct cx23885_tsport *port,
}
}
-void cx23885_shutdown(struct cx23885_dev *dev)
+static void cx23885_shutdown(struct cx23885_dev *dev)
{
/* disable RISC controller */
cx_write(DEV_CNTRL2, 0);
@@ -578,7 +578,7 @@ void cx23885_shutdown(struct cx23885_dev *dev)
}
-void cx23885_reset(struct cx23885_dev *dev)
+static void cx23885_reset(struct cx23885_dev *dev)
{
dprintk(1, "%s()\n", __FUNCTION__);
@@ -594,15 +594,18 @@ void cx23885_reset(struct cx23885_dev *dev)
mdelay(100);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH01 ], 188*4, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH02 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH03 ], 188*4, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH04 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH05 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH06 ], 188*4, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH07 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH08 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH09 ], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
+ 720*4, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH03],
+ 188*4, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH04], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH05], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH06],
+ 188*4, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH07], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH08], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH09], 128, 0);
cx23885_gpio_setup(dev);
}
@@ -637,7 +640,7 @@ static int get_resources(struct cx23885_dev *dev)
static void cx23885_timeout(unsigned long data);
int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
- u32 reg, u32 mask, u32 value);
+ u32 reg, u32 mask, u32 value);
static int cx23885_init_tsport(struct cx23885_dev *dev, struct cx23885_tsport *port, int portno)
{
@@ -704,6 +707,44 @@ static int cx23885_init_tsport(struct cx23885_dev *dev, struct cx23885_tsport *p
return 0;
}
+static void cx23885_dev_checkrevision(struct cx23885_dev *dev)
+{
+ switch (cx_read(RDR_CFG2) & 0xff) {
+ case 0x00:
+ /* cx23885 */
+ dev->hwrevision = 0xa0;
+ break;
+ case 0x01:
+ /* CX23885-12Z */
+ dev->hwrevision = 0xa1;
+ break;
+ case 0x02:
+ /* CX23885-13Z */
+ dev->hwrevision = 0xb0;
+ break;
+ case 0x03:
+ /* CX23888-22Z */
+ dev->hwrevision = 0xc0;
+ break;
+ case 0x0e:
+ /* CX23887-15Z */
+ dev->hwrevision = 0xc0;
+ case 0x0f:
+ /* CX23887-14Z */
+ dev->hwrevision = 0xb1;
+ break;
+ default:
+ printk(KERN_ERR "%s() New hardware revision found 0x%x\n",
+ __FUNCTION__, dev->hwrevision);
+ }
+ if (dev->hwrevision)
+ printk(KERN_INFO "%s() Hardware revision = 0x%02x\n",
+ __FUNCTION__, dev->hwrevision);
+ else
+ printk(KERN_ERR "%s() Hardware revision unknown 0x%x\n",
+ __FUNCTION__, dev->hwrevision);
+}
+
static int cx23885_dev_setup(struct cx23885_dev *dev)
{
int i;
@@ -723,10 +764,14 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
if(dev->pci->device == 0x8880) {
dev->bridge = CX23885_BRIDGE_887;
dev->sram_channels = cx23887_sram_channels;
+ /* Apply a sensible clock frequency for the PCIe bridge */
+ dev->clk_freq = 25000000;
} else
if(dev->pci->device == 0x8852) {
dev->bridge = CX23885_BRIDGE_885;
dev->sram_channels = cx23885_sram_channels;
+ /* Apply a sensible clock frequency for the PCIe bridge */
+ dev->clk_freq = 28000000;
} else
BUG();
@@ -746,6 +791,10 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_card_list(dev);
}
+ /* If the user specific a clk freq override, apply it */
+ if (cx23885_boards[dev->board].clk_freq > 0)
+ dev->clk_freq = cx23885_boards[dev->board].clk_freq;
+
dev->pci_bus = dev->pci->bus->number;
dev->pci_slot = PCI_SLOT(dev->pci->devfn);
dev->pci_irqmask = 0x001f00;
@@ -810,6 +859,17 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_pci_quirks(dev);
+ /* Assume some sensible defaults */
+ dev->tuner_type = cx23885_boards[dev->board].tuner_type;
+ dev->tuner_addr = cx23885_boards[dev->board].tuner_addr;
+ dev->radio_type = cx23885_boards[dev->board].radio_type;
+ dev->radio_addr = cx23885_boards[dev->board].radio_addr;
+
+ dprintk(1, "%s() tuner_type = 0x%x tuner_addr = 0x%x\n",
+ __FUNCTION__, dev->tuner_type, dev->tuner_addr);
+ dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n",
+ __FUNCTION__, dev->radio_type, dev->radio_addr);
+
/* init hardware */
cx23885_reset(dev);
@@ -820,24 +880,33 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_card_setup(dev);
cx23885_ir_init(dev);
- if(cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) {
+ if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) {
+ if (cx23885_video_register(dev) < 0) {
+ printk(KERN_ERR "%s() Failed to register analog "
+ "video adapters on VID_A\n", __FUNCTION__);
+ }
+ }
+
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) {
if (cx23885_dvb_register(&dev->ts1) < 0) {
printk(KERN_ERR "%s() Failed to register dvb adapters on VID_B\n",
__FUNCTION__);
}
}
- if(cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) {
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) {
if (cx23885_dvb_register(&dev->ts2) < 0) {
printk(KERN_ERR "%s() Failed to register dvb adapters on VID_C\n",
__FUNCTION__);
}
}
+ cx23885_dev_checkrevision(dev);
+
return 0;
}
-void cx23885_dev_unregister(struct cx23885_dev *dev)
+static void cx23885_dev_unregister(struct cx23885_dev *dev)
{
release_mem_region(pci_resource_start(dev->pci,0),
pci_resource_len(dev->pci,0));
@@ -845,6 +914,9 @@ void cx23885_dev_unregister(struct cx23885_dev *dev)
if (!atomic_dec_and_test(&dev->refcount))
return;
+ if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO)
+ cx23885_video_unregister(dev);
+
if(cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
cx23885_dvb_unregister(&dev->ts1);
@@ -952,9 +1024,11 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
return 0;
}
-int cx23885_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
- struct scatterlist *sglist, unsigned int bpl,
- unsigned int lines)
+static int cx23885_risc_databuffer(struct pci_dev *pci,
+ struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int bpl,
+ unsigned int lines)
{
u32 instructions;
u32 *rp;
@@ -982,7 +1056,7 @@ int cx23885_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
}
int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
- u32 reg, u32 mask, u32 value)
+ u32 reg, u32 mask, u32 value)
{
u32 *rp;
int rc;
@@ -1011,7 +1085,58 @@ void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf)
videobuf_dma_unmap(q, dma);
videobuf_dma_free(dma);
btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc);
- buf->vb.state = STATE_NEEDS_INIT;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static void cx23885_tsport_reg_dump(struct cx23885_tsport *port)
+{
+ struct cx23885_dev *dev = port->dev;
+
+ dprintk(1, "%s() Register Dump\n", __FUNCTION__);
+ dprintk(1, "%s() DEV_CNTRL2 0x%08X\n", __FUNCTION__,
+ cx_read(DEV_CNTRL2));
+ dprintk(1, "%s() PCI_INT_MSK 0x%08X\n", __FUNCTION__,
+ cx_read(PCI_INT_MSK));
+ dprintk(1, "%s() AUD_INT_INT_MSK 0x%08X\n", __FUNCTION__,
+ cx_read(AUDIO_INT_INT_MSK));
+ dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08X\n", __FUNCTION__,
+ cx_read(AUD_INT_DMA_CTL));
+ dprintk(1, "%s() AUD_EXT_INT_MSK 0x%08X\n", __FUNCTION__,
+ cx_read(AUDIO_EXT_INT_MSK));
+ dprintk(1, "%s() AUD_EXT_DMA_CTL 0x%08X\n", __FUNCTION__,
+ cx_read(AUD_EXT_DMA_CTL));
+ dprintk(1, "%s() PAD_CTRL 0x%08X\n", __FUNCTION__,
+ cx_read(PAD_CTRL));
+ dprintk(1, "%s() ALT_PIN_OUT_SEL 0x%08X\n", __FUNCTION__,
+ cx_read(ALT_PIN_OUT_SEL));
+ dprintk(1, "%s() GPIO2 0x%08X\n", __FUNCTION__,
+ cx_read(GPIO2));
+ dprintk(1, "%s() gpcnt(0x%08X) 0x%08X\n", __FUNCTION__,
+ port->reg_gpcnt, cx_read(port->reg_gpcnt));
+ dprintk(1, "%s() gpcnt_ctl(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_gpcnt_ctl, cx_read(port->reg_gpcnt_ctl));
+ dprintk(1, "%s() dma_ctl(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_dma_ctl, cx_read(port->reg_dma_ctl));
+ dprintk(1, "%s() src_sel(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_src_sel, cx_read(port->reg_src_sel));
+ dprintk(1, "%s() lngth(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_lngth, cx_read(port->reg_lngth));
+ dprintk(1, "%s() hw_sop_ctrl(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_hw_sop_ctrl, cx_read(port->reg_hw_sop_ctrl));
+ dprintk(1, "%s() gen_ctrl(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_gen_ctrl, cx_read(port->reg_gen_ctrl));
+ dprintk(1, "%s() bd_pkt_status(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_bd_pkt_status, cx_read(port->reg_bd_pkt_status));
+ dprintk(1, "%s() sop_status(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_sop_status, cx_read(port->reg_sop_status));
+ dprintk(1, "%s() fifo_ovfl_stat(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_fifo_ovfl_stat, cx_read(port->reg_fifo_ovfl_stat));
+ dprintk(1, "%s() vld_misc(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_vld_misc, cx_read(port->reg_vld_misc));
+ dprintk(1, "%s() ts_clk_en(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_ts_clk_en, cx_read(port->reg_ts_clk_en));
+ dprintk(1, "%s() ts_int_msk(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_ts_int_msk, cx_read(port->reg_ts_int_msk));
}
static int cx23885_start_dma(struct cx23885_tsport *port,
@@ -1076,6 +1201,9 @@ static int cx23885_start_dma(struct cx23885_tsport *port,
cx_set(DEV_CNTRL2, (1<<5)); /* Enable RISC controller */
+ if (debug > 4)
+ cx23885_tsport_reg_dump(port);
+
return 0;
}
@@ -1091,7 +1219,7 @@ static int cx23885_stop_dma(struct cx23885_tsport *port)
return 0;
}
-static int cx23885_restart_queue(struct cx23885_tsport *port,
+int cx23885_restart_queue(struct cx23885_tsport *port,
struct cx23885_dmaqueue *q)
{
struct cx23885_dev *dev = port->dev;
@@ -1114,7 +1242,7 @@ static int cx23885_restart_queue(struct cx23885_tsport *port,
list_del(&buf->vb.queue);
list_add_tail(&buf->vb.queue, &q->active);
cx23885_start_dma(port, q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(5, "[%p/%d] restart_queue - first active\n",
@@ -1125,7 +1253,7 @@ static int cx23885_restart_queue(struct cx23885_tsport *port,
prev->fmt == buf->fmt) {
list_del(&buf->vb.queue);
list_add_tail(&buf->vb.queue, &q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
prev->risc.jmp[2] = cpu_to_le32(0); /* 64 bit bits 63-32 */
@@ -1162,7 +1290,7 @@ int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port,
if (0 != buf->vb.baddr && buf->vb.bsize < size)
return -EINVAL;
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
buf->vb.width = port->ts_packet_size;
buf->vb.height = port->ts_packet_count;
buf->vb.size = size;
@@ -1174,7 +1302,7 @@ int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port,
videobuf_to_dma(&buf->vb)->sglist,
buf->vb.width, buf->vb.height);
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
@@ -1197,7 +1325,7 @@ void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf)
dprintk( 1, "queue is empty - first active\n" );
list_add_tail(&buf->vb.queue, &cx88q->active);
cx23885_start_dma(port, cx88q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = cx88q->count++;
mod_timer(&cx88q->timeout, jiffies + BUFFER_TIMEOUT);
dprintk(1, "[%p/%d] %s - first active\n",
@@ -1207,7 +1335,7 @@ void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf)
prev = list_entry(cx88q->active.prev, struct cx23885_buffer,
vb.queue);
list_add_tail(&buf->vb.queue, &cx88q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = cx88q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
prev->risc.jmp[2] = cpu_to_le32(0); /* 64 bit bits 63-32 */
@@ -1231,7 +1359,7 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason,
buf = list_entry(q->active.next, struct cx23885_buffer,
vb.queue);
list_del(&buf->vb.queue);
- buf->vb.state = STATE_ERROR;
+ buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
dprintk(1, "[%p/%d] %s - dma=0x%08lx\n",
buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
@@ -1243,16 +1371,6 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason,
spin_unlock_irqrestore(&port->slock, flags);
}
-void cx23885_cancel_buffers(struct cx23885_tsport *port)
-{
- struct cx23885_dev *dev = port->dev;
- struct cx23885_dmaqueue *q = &port->mpegq;
-
- dprintk(1, "%s()\n", __FUNCTION__);
- del_timer_sync(&q->timeout);
- cx23885_stop_dma(port);
- do_cancel_buffers(port, "cancel", 0);
-}
static void cx23885_timeout(unsigned long data)
{
@@ -1325,12 +1443,15 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
struct cx23885_tsport *ts1 = &dev->ts1;
struct cx23885_tsport *ts2 = &dev->ts2;
u32 pci_status, pci_mask;
+ u32 vida_status, vida_mask;
u32 ts1_status, ts1_mask;
u32 ts2_status, ts2_mask;
- int ts1_count = 0, ts2_count = 0, handled = 0;
+ int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
pci_status = cx_read(PCI_INT_STAT);
pci_mask = cx_read(PCI_INT_MSK);
+ vida_status = cx_read(VID_A_INT_STAT);
+ vida_mask = cx_read(VID_A_INT_MSK);
ts1_status = cx_read(VID_B_INT_STAT);
ts1_mask = cx_read(VID_B_INT_MSK);
ts2_status = cx_read(VID_C_INT_STAT);
@@ -1339,11 +1460,17 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
if ( (pci_status == 0) && (ts2_status == 0) && (ts1_status == 0) )
goto out;
+ vida_count = cx_read(VID_A_GPCNT);
ts1_count = cx_read(ts1->reg_gpcnt);
ts2_count = cx_read(ts2->reg_gpcnt);
- dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n", pci_status, pci_mask );
- dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n", ts1_status, ts1_mask, ts1_count );
- dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n", ts2_status, ts2_mask, ts2_count );
+ dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n",
+ pci_status, pci_mask);
+ dprintk(7, "vida_status: 0x%08x vida_mask: 0x%08x count: 0x%x\n",
+ vida_status, vida_mask, vida_count);
+ dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n",
+ ts1_status, ts1_mask, ts1_count);
+ dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n",
+ ts2_status, ts2_mask, ts2_count);
if ( (pci_status & PCI_MSK_RISC_RD) ||
(pci_status & PCI_MSK_RISC_WR) ||
@@ -1380,11 +1507,18 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
}
- if (ts1_status)
- handled += cx23885_irq_ts(ts1, ts1_status);
+ if (ts1_status) {
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
+ handled += cx23885_irq_ts(ts1, ts1_status);
+ }
+
+ if (ts2_status) {
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB)
+ handled += cx23885_irq_ts(ts2, ts2_status);
+ }
- if (ts2_status)
- handled += cx23885_irq_ts(ts2, ts2_status);
+ if (vida_status)
+ handled += cx23885_video_irq(dev, vida_status);
if (handled)
cx_write(PCI_INT_STAT, pci_status);
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index eda8c05d0931..ed465c007cea 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -32,13 +32,26 @@
#include "s5h1409.h"
#include "mt2131.h"
+#include "tda8290.h"
+#include "tda18271.h"
#include "lgdt330x.h"
+#include "xc5000.h"
#include "dvb-pll.h"
+#include "tuner-xc2028.h"
+#include "tuner-xc2028-types.h"
-static unsigned int debug = 0;
+static unsigned int debug;
-#define dprintk(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg)
+#define dprintk(level, fmt, arg...)\
+ do { if (debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int alt_tuner;
+module_param(alt_tuner, int, 0644);
+MODULE_PARM_DESC(alt_tuner, "Enable alternate tuner configuration");
/* ------------------------------------------------------------------ */
@@ -85,18 +98,39 @@ static struct s5h1409_config hauppauge_generic_config = {
.demod_address = 0x32 >> 1,
.output_mode = S5H1409_SERIAL_OUTPUT,
.gpio = S5H1409_GPIO_ON,
- .if_freq = 44000,
+ .qam_if = 44000,
.inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1409_config hauppauge_ezqam_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .qam_if = 4000,
+ .inversion = S5H1409_INVERSION_ON,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
};
static struct s5h1409_config hauppauge_hvr1800lp_config = {
.demod_address = 0x32 >> 1,
.output_mode = S5H1409_SERIAL_OUTPUT,
.gpio = S5H1409_GPIO_OFF,
- .if_freq = 44000,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1409_config hauppauge_hvr1500_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
.inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
};
static struct mt2131_config hauppauge_generic_tunerconfig = {
@@ -109,6 +143,66 @@ static struct lgdt330x_config fusionhdtv_5_express = {
.serial_mpeg = 0x40,
};
+static struct s5h1409_config hauppauge_hvr1500q_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct xc5000_config hauppauge_hvr1500q_tunerconfig = {
+ .i2c_address = 0x61,
+ .if_khz = 5380,
+ .tuner_callback = cx23885_tuner_callback
+};
+
+static struct tda829x_config tda829x_no_probe = {
+ .probe_tuner = TDA829X_DONT_PROBE,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 5380, .std_bits = 0x1b },
+ .qam_6 = { .if_freq = 4000, .std_bits = 0x18 },
+};
+
+static struct tda18271_config hauppauge_tda18271_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+};
+
+static int cx23885_hvr1500_xc3028_callback(void *ptr, int command, int arg)
+{
+ struct cx23885_tsport *port = ptr;
+ struct cx23885_dev *dev = port->dev;
+
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ /* Send the tuner in then out of reset */
+ /* GPIO-2 xc3028 tuner */
+ dprintk(1, "%s: XC2028_TUNER_RESET %d\n", __FUNCTION__, arg);
+
+ cx_set(GP0_IO, 0x00040000);
+ cx_clear(GP0_IO, 0x00000004);
+ msleep(5);
+
+ cx_set(GP0_IO, 0x00040004);
+ msleep(5);
+ break;
+ case XC2028_RESET_CLK:
+ dprintk(1, "%s: XC2028_RESET_CLK %d\n", __FUNCTION__, arg);
+ break;
+ default:
+ dprintk(1, "%s: unknown command %d, arg %d\n", __FUNCTION__,
+ command, arg);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int dvb_register(struct cx23885_tsport *port)
{
struct cx23885_dev *dev = port->dev;
@@ -120,7 +214,6 @@ static int dvb_register(struct cx23885_tsport *port)
/* init frontend */
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
- case CX23885_BOARD_HAUPPAUGE_HVR1800:
i2c_bus = &dev->i2c_bus[0];
port->dvb.frontend = dvb_attach(s5h1409_attach,
&hauppauge_generic_config,
@@ -131,6 +224,36 @@ static int dvb_register(struct cx23885_tsport *port)
&hauppauge_generic_tunerconfig, 0);
}
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ i2c_bus = &dev->i2c_bus[0];
+ switch (alt_tuner) {
+ case 1:
+ port->dvb.frontend =
+ dvb_attach(s5h1409_attach,
+ &hauppauge_ezqam_config,
+ &i2c_bus->i2c_adap);
+ if (port->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, port->dvb.frontend,
+ &dev->i2c_bus[1].i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, port->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_tda18271_config);
+ }
+ break;
+ case 0:
+ default:
+ port->dvb.frontend =
+ dvb_attach(s5h1409_attach,
+ &hauppauge_generic_config,
+ &i2c_bus->i2c_adap);
+ if (port->dvb.frontend != NULL)
+ dvb_attach(mt2131_attach, port->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_generic_tunerconfig, 0);
+ break;
+ }
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
i2c_bus = &dev->i2c_bus[0];
port->dvb.frontend = dvb_attach(s5h1409_attach,
@@ -152,6 +275,43 @@ static int dvb_register(struct cx23885_tsport *port)
&i2c_bus->i2c_adap, DVB_PLL_LG_TDVS_H06XF);
}
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ i2c_bus = &dev->i2c_bus[1];
+ 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;
+ dvb_attach(xc5000_attach, port->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_hvr1500q_tunerconfig);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ i2c_bus = &dev->i2c_bus[1];
+ port->dvb.frontend = dvb_attach(s5h1409_attach,
+ &hauppauge_hvr1500_config,
+ &dev->i2c_bus[0].i2c_adap);
+ if (port->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &i2c_bus->i2c_adap,
+ .i2c_addr = 0x61,
+ .video_dev = port,
+ .callback = cx23885_hvr1500_xc3028_callback,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = "xc3028-v27.fw",
+ .max_len = 64,
+ .scode_table = OREN538,
+ };
+
+ fe = dvb_attach(xc2028_attach,
+ port->dvb.frontend, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
default:
printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
dev->name);
@@ -165,6 +325,9 @@ static int dvb_register(struct cx23885_tsport *port)
/* Put the analog decoder in standby to keep it quiet */
cx23885_call_i2c_clients(i2c_bus, TUNER_SET_STANDBY, NULL);
+ if (port->dvb.frontend->ops.analog_ops.standby)
+ port->dvb.frontend->ops.analog_ops.standby(port->dvb.frontend);
+
/* register everything */
return videobuf_dvb_register(&port->dvb, THIS_MODULE, port,
&dev->pci->dev);
diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c
index 71da528932df..92fe0bd37c84 100644
--- a/drivers/media/video/cx23885/cx23885-i2c.c
+++ b/drivers/media/video/cx23885/cx23885-i2c.c
@@ -29,7 +29,7 @@
#include <media/v4l2-common.h>
-static unsigned int i2c_debug = 0;
+static unsigned int i2c_debug;
module_param(i2c_debug, int, 0644);
MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
@@ -37,8 +37,10 @@ static unsigned int i2c_scan = 0;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
-#define dprintk(level,fmt, arg...) if (i2c_debug >= level) \
- printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg)
+#define dprintk(level, fmt, arg...)\
+ do { if (i2c_debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
#define I2C_WAIT_DELAY 32
#define I2C_WAIT_RETRY 64
@@ -77,14 +79,19 @@ static int i2c_wait_done(struct i2c_adapter *i2c_adap)
}
static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
- const struct i2c_msg *msg, int last)
+ const struct i2c_msg *msg, int joined_rlen)
{
struct cx23885_i2c *bus = i2c_adap->algo_data;
struct cx23885_dev *dev = bus->dev;
u32 wdata, addr, ctrl;
int retval, cnt;
- dprintk(1, "%s()\n", __FUNCTION__);
+ if (joined_rlen)
+ dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __FUNCTION__,
+ msg->len, joined_rlen);
+ else
+ dprintk(1, "%s(msg->len=%d)\n", __FUNCTION__, msg->len);
+
/* Deal with i2c probe functions with zero payload */
if (msg->len == 0) {
cx_write(bus->reg_addr, msg->addr << 25);
@@ -106,6 +113,8 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
if (msg->len > 1)
ctrl |= I2C_NOSTOP | I2C_EXTEND;
+ else if (joined_rlen)
+ ctrl |= I2C_NOSTOP;
cx_write(bus->reg_addr, addr);
cx_write(bus->reg_wdata, wdata);
@@ -127,8 +136,10 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
wdata = msg->buf[cnt];
ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
- if (cnt < msg->len-1 || !last)
+ if (cnt < msg->len - 1)
ctrl |= I2C_NOSTOP | I2C_EXTEND;
+ else if (joined_rlen)
+ ctrl |= I2C_NOSTOP;
cx_write(bus->reg_addr, addr);
cx_write(bus->reg_wdata, wdata);
@@ -150,19 +161,22 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
eio:
retval = -EIO;
err:
- printk(" ERR: %d\n", retval);
+ if (i2c_debug)
+ printk(" ERR: %d\n", retval);
return retval;
}
static int i2c_readbytes(struct i2c_adapter *i2c_adap,
- const struct i2c_msg *msg, int last)
+ const struct i2c_msg *msg, int joined)
{
struct cx23885_i2c *bus = i2c_adap->algo_data;
struct cx23885_dev *dev = bus->dev;
u32 ctrl, cnt;
int retval;
- dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (i2c_debug && !joined)
+ dprintk(1, "%s(msg->len=%d)\n", __FUNCTION__, msg->len);
/* Deal with i2c probe functions with zero payload */
if (msg->len == 0) {
@@ -178,11 +192,18 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
return 0;
}
+ if (i2c_debug) {
+ if (joined)
+ printk(" R");
+ else
+ printk(" <R %02x", (msg->addr << 1) + 1);
+ }
+
for(cnt = 0; cnt < msg->len; cnt++) {
ctrl = bus->i2c_period | (1 << 12) | (1 << 2) | 1;
- if (cnt < msg->len-1 || !last)
+ if (cnt < msg->len - 1)
ctrl |= I2C_NOSTOP | I2C_EXTEND;
cx_write(bus->reg_addr, msg->addr << 25);
@@ -195,9 +216,7 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
goto eio;
msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
if (i2c_debug) {
- if (!(ctrl & I2C_NOSTOP))
- printk(" <R %02x", (msg->addr << 1) +1);
- printk(" =%02x", msg->buf[cnt]);
+ printk(" %02x", msg->buf[cnt]);
if (!(ctrl & I2C_NOSTOP))
printk(" >\n");
}
@@ -207,7 +226,8 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
eio:
retval = -EIO;
err:
- printk(" ERR: %d\n", retval);
+ if (i2c_debug)
+ printk(" ERR: %d\n", retval);
return retval;
}
@@ -225,15 +245,22 @@ static int i2c_xfer(struct i2c_adapter *i2c_adap,
__FUNCTION__, num, msgs[i].addr, msgs[i].len);
if (msgs[i].flags & I2C_M_RD) {
/* read */
- retval = i2c_readbytes(i2c_adap, &msgs[i], i+1 == num);
+ retval = i2c_readbytes(i2c_adap, &msgs[i], 0);
+ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
+ msgs[i].addr == msgs[i + 1].addr) {
+ /* write then read from same address */
+ retval = i2c_sendbytes(i2c_adap, &msgs[i],
+ msgs[i + 1].len);
if (retval < 0)
goto err;
+ i++;
+ retval = i2c_readbytes(i2c_adap, &msgs[i], 1);
} else {
/* write */
- retval = i2c_sendbytes(i2c_adap, &msgs[i], i+1 == num);
- if (retval < 0)
- goto err;
+ retval = i2c_sendbytes(i2c_adap, &msgs[i], 0);
}
+ if (retval < 0)
+ goto err;
}
return num;
@@ -243,7 +270,9 @@ static int i2c_xfer(struct i2c_adapter *i2c_adap,
static int attach_inform(struct i2c_client *client)
{
- struct cx23885_dev *dev = i2c_get_adapdata(client->adapter);
+ struct cx23885_i2c *bus = i2c_get_adapdata(client->adapter);
+ struct cx23885_dev *dev = bus->dev;
+ struct tuner_setup tun_setup;
dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
client->driver->driver.name, client->addr, client->name);
@@ -251,6 +280,31 @@ static int attach_inform(struct i2c_client *client)
if (!client->driver->command)
return 0;
+ if (dev->tuner_type != UNSET) {
+
+ dprintk(1, "%s (tuner) i2c attach [addr=0x%x,client=%s]\n",
+ client->driver->driver.name, client->addr,
+ client->name);
+
+ if ((dev->tuner_addr == ADDR_UNSET) ||
+ (dev->tuner_addr == client->addr)) {
+
+ dprintk(1, "%s (tuner || addr UNSET)\n",
+ client->driver->driver.name);
+
+ dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
+ client->driver->driver.name,
+ client->addr, client->name);
+
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = dev->tuner_addr;
+
+ client->driver->command(client, TUNER_SET_TYPE_ADDR,
+ &tun_setup);
+ }
+ }
+
return 0;
}
@@ -289,6 +343,7 @@ static struct i2c_adapter cx23885_i2c_adap_template = {
.owner = THIS_MODULE,
.id = I2C_HW_B_CX23885,
.algo = &cx23885_i2c_algo_template,
+ .class = I2C_CLASS_TV_ANALOG,
.client_register = attach_inform,
.client_unregister = detach_inform,
};
@@ -305,7 +360,7 @@ static char *i2c_devs[128] = {
[ 0x84 >> 1 ] = "tda8295",
[ 0xa0 >> 1 ] = "eeprom",
[ 0xc0 >> 1 ] = "tuner/mt2131/tda8275",
- [ 0xc2 >> 1 ] = "tuner/mt2131/tda8275",
+ [ 0xc2 >> 1 ] = "tuner/mt2131/tda8275/xc5000",
};
static void do_i2c_scan(char *name, struct i2c_client *c)
@@ -344,6 +399,7 @@ int cx23885_i2c_register(struct cx23885_i2c *bus)
bus->i2c_algo.data = bus;
bus->i2c_adap.algo_data = bus;
+ i2c_set_adapdata(&bus->i2c_adap, bus);
i2c_add_adapter(&bus->i2c_adap);
bus->i2c_client.adapter = &bus->i2c_adap;
@@ -366,8 +422,6 @@ int cx23885_i2c_unregister(struct cx23885_i2c *bus)
/* ----------------------------------------------------------------------- */
-EXPORT_SYMBOL(cx23885_call_i2c_clients);
-
/*
* Local variables:
* c-basic-offset: 8
diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h
index 162169f9091b..bdd11bc513ad 100644
--- a/drivers/media/video/cx23885/cx23885-reg.h
+++ b/drivers/media/video/cx23885/cx23885-reg.h
@@ -233,6 +233,17 @@ Channel manager Data Structure entry = 20 DWORD
#define VID_A_INT_SSTAT 0x0004002C
#define VID_B_INT_MSK 0x00040030
+#define VID_B_MSK_BAD_PKT (1 << 20)
+#define VID_B_MSK_VBI_OPC_ERR (1 << 17)
+#define VID_B_MSK_OPC_ERR (1 << 16)
+#define VID_B_MSK_VBI_SYNC (1 << 13)
+#define VID_B_MSK_SYNC (1 << 12)
+#define VID_B_MSK_VBI_OF (1 << 9)
+#define VID_B_MSK_OF (1 << 8)
+#define VID_B_MSK_VBI_RISCI2 (1 << 5)
+#define VID_B_MSK_RISCI2 (1 << 4)
+#define VID_B_MSK_VBI_RISCI1 (1 << 1)
+#define VID_B_MSK_RISCI1 1
#define VID_B_INT_STAT 0x00040034
#define VID_B_INT_MSTAT 0x00040038
#define VID_B_INT_SSTAT 0x0004003C
@@ -276,6 +287,7 @@ Channel manager Data Structure entry = 20 DWORD
#define RDR_CFG0 0x00050000
#define RDR_CFG1 0x00050004
+#define RDR_CFG2 0x00050008
#define RDR_TLCTL0 0x00050318
/* APB DMAC Current Buffer Pointer */
@@ -335,6 +347,7 @@ Channel manager Data Structure entry = 20 DWORD
/* GPIO (417 Microsoftcontroller) Output Enable, Low Active */
#define MC417_OEN 0x00110024
#define MC417_CTL 0x00110028
+#define ALT_PIN_OUT_SEL 0x0011002C
#define CLK_DELAY 0x00110048
#define PAD_CTRL 0x0011004C
diff --git a/drivers/media/video/cx23885/cx23885-vbi.c b/drivers/media/video/cx23885/cx23885-vbi.c
new file mode 100644
index 000000000000..e36e3fcae2fb
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-vbi.c
@@ -0,0 +1,258 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2007 Steven Toth <stoth@hauppauge.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include "cx23885.h"
+
+static unsigned int vbibufs = 4;
+module_param(vbibufs, int, 0644);
+MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
+
+static unsigned int vbi_debug;
+module_param(vbi_debug, int, 0644);
+MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]");
+
+#define dprintk(level, fmt, arg...)\
+ do { if (vbi_debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
+
+/* ------------------------------------------------------------------ */
+
+int cx23885_vbi_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (dev->tvnorm & V4L2_STD_525_60) {
+ /* ntsc */
+ f->fmt.vbi.sampling_rate = 28636363;
+ f->fmt.vbi.start[0] = 10;
+ f->fmt.vbi.start[1] = 273;
+
+ } else if (dev->tvnorm & V4L2_STD_625_50) {
+ /* pal */
+ f->fmt.vbi.sampling_rate = 35468950;
+ f->fmt.vbi.start[0] = 7 - 1;
+ f->fmt.vbi.start[1] = 319 - 1;
+ }
+ return 0;
+}
+
+static int cx23885_start_vbi_dma(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q,
+ struct cx23885_buffer *buf)
+{
+ /* setup fifo + format */
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02],
+ buf->vb.width, buf->risc.dma);
+
+ /* reset counter */
+ q->count = 1;
+
+ /* enable irqs */
+ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+ cx_set(VID_A_INT_MSK, 0x000022);
+
+ /* start dma */
+ cx_set(DEV_CNTRL2, (1<<5));
+ cx_set(VID_A_DMA_CTL, 0x00000022);
+
+ return 0;
+}
+
+int cx23885_stop_vbi_dma(struct cx23885_dev *dev)
+{
+ /* stop dma */
+ cx_clear(VID_A_DMA_CTL, 0x00000022);
+
+ /* disable irqs */
+ cx_clear(PCI_INT_MSK, 0x000001);
+ cx_clear(VID_A_INT_MSK, 0x00000022);
+ return 0;
+}
+
+int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q)
+{
+ struct cx23885_buffer *buf;
+ struct list_head *item;
+
+ if (list_empty(&q->active))
+ return 0;
+
+ buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue);
+ dprintk(2, "restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ cx23885_start_vbi_dma(dev, q, buf);
+ list_for_each(item, &q->active) {
+ buf = list_entry(item, struct cx23885_buffer, vb.queue);
+ buf->count = q->count++;
+ }
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+void cx23885_vbi_timeout(unsigned long data)
+{
+ struct cx23885_dev *dev = (struct cx23885_dev *)data;
+ struct cx23885_dmaqueue *q = &dev->vbiq;
+ struct cx23885_buffer *buf;
+ unsigned long flags;
+
+ cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH02]);
+
+ cx_clear(VID_A_DMA_CTL, 0x22);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx23885_buffer,
+ vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->name,
+ buf, buf->vb.i, (unsigned long)buf->risc.dma);
+ }
+ cx23885_restart_vbi_queue(dev, q);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+/* ------------------------------------------------------------------ */
+#define VBI_LINE_LENGTH 2048
+#define VBI_LINE_COUNT 17
+
+static int
+vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ *size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
+ if (0 == *count)
+ *count = vbibufs;
+ if (*count < 2)
+ *count = 2;
+ if (*count > 32)
+ *count = 32;
+ return 0;
+}
+
+static int
+vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+ unsigned int size;
+ int rc;
+
+ size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = VBI_LINE_LENGTH;
+ buf->vb.height = VBI_LINE_COUNT;
+ buf->vb.size = size;
+ buf->vb.field = V4L2_FIELD_SEQ_TB;
+
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc)
+ goto fail;
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->vb.width * buf->vb.height,
+ buf->vb.width, 0,
+ buf->vb.height);
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx23885_free_buffer(q, buf);
+ return rc;
+}
+
+static void
+vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+ struct cx23885_buffer *prev;
+ struct cx23885_fh *fh = vq->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_dmaqueue *q = &dev->vbiq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx23885_start_vbi_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] vbi_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else {
+ prev = list_entry(q->active.prev, struct cx23885_buffer,
+ vb.queue);
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63-32 */
+ dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+ }
+}
+
+static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+
+ cx23885_free_buffer(q, buf);
+}
+
+struct videobuf_queue_ops cx23885_vbi_qops = {
+ .buf_setup = vbi_setup,
+ .buf_prepare = vbi_prepare,
+ .buf_queue = vbi_queue,
+ .buf_release = vbi_release,
+};
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
new file mode 100644
index 000000000000..d3c4d2c5cbe0
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -0,0 +1,1557 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2007 Steven Toth <stoth@hauppauge.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <asm/div64.h>
+
+#include "cx23885.h"
+#include <media/v4l2-common.h>
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+/* Include V4L1 specific functions. Should be removed soon */
+#include <linux/videodev.h>
+#endif
+
+MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
+MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int video_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+static unsigned int video_debug;
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
+
+static unsigned int irq_debug;
+module_param(irq_debug, int, 0644);
+MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
+
+static unsigned int vid_limit = 16;
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
+#define dprintk(level, fmt, arg...)\
+ do { if (video_debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
+
+/* ------------------------------------------------------------------- */
+/* static data */
+
+#define FORMAT_FLAGS_PACKED 0x01
+
+static struct cx23885_fmt formats[] = {
+ {
+ .name = "8 bpp, gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .depth = 24,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },
+};
+
+static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+
+ printk(KERN_ERR "%s(0x%08x) NOT FOUND\n", __FUNCTION__, fourcc);
+ return NULL;
+}
+
+/* ------------------------------------------------------------------- */
+
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
+static struct cx23885_ctrl cx23885_ctls[] = {
+ /* --- video --- */
+ {
+ .v = {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = LUMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ .v = {
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = LUMA_CTRL,
+ .mask = 0xff00,
+ .shift = 8,
+ }, {
+ .v = {
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = CHROMA_CTRL,
+ .mask = 0xff0000,
+ .shift = 16,
+ }, {
+ /* strictly, this only describes only U saturation.
+ * V saturation is handled specially through code.
+ */
+ .v = {
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = CHROMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ /* --- audio --- */
+ .v = {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ .reg = PATH1_CTL1,
+ .mask = (0x1f << 24),
+ .shift = 24,
+ }, {
+ .v = {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 0x3f,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .reg = PATH1_VOL_CTL,
+ .mask = 0xff,
+ .shift = 0,
+ }
+};
+static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls);
+
+const u32 cx23885_user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_AUDIO_VOLUME,
+ V4L2_CID_AUDIO_MUTE,
+ 0
+};
+EXPORT_SYMBOL(cx23885_user_ctrls);
+
+static const u32 *ctrl_classes[] = {
+ cx23885_user_ctrls,
+ NULL
+};
+
+void cx23885_video_wakeup(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q, u32 count)
+{
+ struct cx23885_buffer *buf;
+ int bc;
+
+ for (bc = 0;; bc++) {
+ if (list_empty(&q->active))
+ break;
+ buf = list_entry(q->active.next,
+ struct cx23885_buffer, vb.queue);
+
+ /* count comes from the hw and is is 16bit wide --
+ * this trick handles wrap-arounds correctly for
+ * up to 32767 buffers in flight... */
+ if ((s16) (count - buf->count) < 0)
+ break;
+
+ do_gettimeofday(&buf->vb.ts);
+ dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i,
+ count, buf->count);
+ buf->vb.state = VIDEOBUF_DONE;
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ }
+ if (list_empty(&q->active)) {
+ del_timer(&q->timeout);
+ } else {
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ }
+ if (bc != 1)
+ printk(KERN_ERR "%s: %d buffers handled (should be 1)\n",
+ __FUNCTION__, bc);
+}
+
+int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
+{
+ dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
+ __FUNCTION__,
+ (unsigned int)norm,
+ v4l2_norm_to_name(norm));
+
+ dev->tvnorm = norm;
+
+ /* Tell the analog tuner/demods */
+ cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_STD, &norm);
+
+ /* Tell the internal A/V decoder */
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_STD, &norm);
+
+ return 0;
+}
+
+struct video_device *cx23885_vdev_init(struct cx23885_dev *dev,
+ struct pci_dev *pci,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->minor = -1;
+ vfd->dev = &pci->dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
+ dev->name, type, cx23885_boards[dev->board].name);
+ return vfd;
+}
+
+int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl)
+{
+ int i;
+
+ if (qctrl->id < V4L2_CID_BASE ||
+ qctrl->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ for (i = 0; i < CX23885_CTLS; i++)
+ if (cx23885_ctls[i].v.id == qctrl->id)
+ break;
+ if (i == CX23885_CTLS) {
+ *qctrl = no_ctl;
+ return 0;
+ }
+ *qctrl = cx23885_ctls[i].v;
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_ctrl_query);
+
+/* ------------------------------------------------------------------- */
+/* resource management */
+
+static int res_get(struct cx23885_dev *dev, struct cx23885_fh *fh,
+ unsigned int bit)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&dev->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dprintk(1, "res: get %d\n", bit);
+ mutex_unlock(&dev->lock);
+ return 1;
+}
+
+static int res_check(struct cx23885_fh *fh, unsigned int bit)
+{
+ return (fh->resources & bit);
+}
+
+static int res_locked(struct cx23885_dev *dev, unsigned int bit)
+{
+ return (dev->resources & bit);
+}
+
+static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh,
+ unsigned int bits)
+{
+ BUG_ON((fh->resources & bits) != bits);
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ mutex_lock(&dev->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dprintk(1, "res: put %d\n", bits);
+ mutex_unlock(&dev->lock);
+}
+
+int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
+{
+ struct v4l2_routing route;
+ memset(&route, 0, sizeof(route));
+
+ dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
+ __FUNCTION__,
+ input, INPUT(input)->vmux,
+ INPUT(input)->gpio0, INPUT(input)->gpio1,
+ INPUT(input)->gpio2, INPUT(input)->gpio3);
+ dev->input = input;
+
+ route.input = INPUT(input)->vmux;
+
+ /* Tell the internal A/V decoder */
+ cx23885_call_i2c_clients(&dev->i2c_bus[2],
+ VIDIOC_INT_S_VIDEO_ROUTING, &route);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_video_mux);
+
+/* ------------------------------------------------------------------ */
+int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width,
+ unsigned int height, enum v4l2_field field)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ return 0;
+}
+
+static int cx23885_start_video_dma(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q,
+ struct cx23885_buffer *buf)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ /* setup fifo + format */
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
+ buf->bpl, buf->risc.dma);
+ cx23885_set_scale(dev, buf->vb.width, buf->vb.height, buf->vb.field);
+
+ /* reset counter */
+ cx_write(VID_A_GPCNT_CTL, 3);
+ q->count = 1;
+
+ /* enable irq */
+ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+ cx_set(VID_A_INT_MSK, 0x000011);
+
+ /* start dma */
+ cx_set(DEV_CNTRL2, (1<<5));
+ cx_set(VID_A_DMA_CTL, 0x11); /* FIFO and RISC enable */
+
+ return 0;
+}
+
+
+static int cx23885_restart_video_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q)
+{
+ struct cx23885_buffer *buf, *prev;
+ struct list_head *item;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx23885_buffer,
+ vb.queue);
+ dprintk(2, "restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ cx23885_start_video_dma(dev, q, buf);
+ list_for_each(item, &q->active) {
+ buf = list_entry(item, struct cx23885_buffer,
+ vb.queue);
+ buf->count = q->count++;
+ }
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+ }
+
+ prev = NULL;
+ for (;;) {
+ if (list_empty(&q->queued))
+ return 0;
+ buf = list_entry(q->queued.next, struct cx23885_buffer,
+ vb.queue);
+ if (NULL == prev) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ cx23885_start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] restart_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
+ dprintk(2, "[%p/%d] restart_queue - move to active\n",
+ buf, buf->vb.i);
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+}
+
+static int buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ struct cx23885_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth*fh->width*fh->height >> 3;
+ if (0 == *count)
+ *count = 32;
+ while (*size * *count > vid_limit * 1024 * 1024)
+ (*count)--;
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+ int rc, init_buffer = 0;
+ u32 line0_offset, line1_offset;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+ BUG_ON(NULL == fh->fmt);
+ if (fh->width < 48 || fh->width > norm_maxw(dev->tvnorm) ||
+ fh->height < 32 || fh->height > norm_maxh(dev->tvnorm))
+ return -EINVAL;
+ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ init_buffer = 1;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ init_buffer = 1;
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc)
+ goto fail;
+ }
+
+ if (init_buffer) {
+ buf->bpl = buf->vb.width * buf->fmt->depth >> 3;
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, 0, UNSET,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, UNSET, 0,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ if (dev->tvnorm & V4L2_STD_NTSC) {
+ /* cx25840 transmits NTSC bottom field first */
+ dprintk(1, "%s() Creating NTSC risc\n",
+ __FUNCTION__);
+ line0_offset = buf->bpl;
+ line1_offset = 0;
+ } else {
+ /* All other formats are top field first */
+ dprintk(1, "%s() Creating PAL/SECAM risc\n",
+ __FUNCTION__);
+ line0_offset = 0;
+ line1_offset = buf->bpl;
+ }
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, line0_offset,
+ line1_offset,
+ buf->bpl, buf->bpl,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->bpl * (buf->vb.height >> 1),
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ buf->bpl * (buf->vb.height >> 1), 0,
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ default:
+ BUG();
+ }
+ }
+ dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
+ buf, buf->vb.i,
+ fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
+ (unsigned long)buf->risc.dma);
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx23885_free_buffer(q, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+ struct cx23885_buffer *prev;
+ struct cx23885_fh *fh = vq->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_dmaqueue *q = &dev->vidq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ if (!list_empty(&q->queued)) {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - append to queued\n",
+ buf, buf->vb.i);
+
+ } else if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx23885_start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] buffer_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else {
+ prev = list_entry(q->active.prev, struct cx23885_buffer,
+ vb.queue);
+ if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ /* 64 bit bits 63-32 */
+ prev->risc.jmp[2] = cpu_to_le32(0);
+ dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+
+ } else {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - first queued\n",
+ buf, buf->vb.i);
+ }
+ }
+}
+
+static void buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+
+ cx23885_free_buffer(q, buf);
+}
+
+static struct videobuf_queue_ops cx23885_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+static struct videobuf_queue *get_queue(struct cx23885_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &fh->vidq;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return &fh->vbiq;
+ default:
+ BUG();
+ return NULL;
+ }
+}
+
+static int get_resource(struct cx23885_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return RESOURCE_VIDEO;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return RESOURCE_VBI;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static int video_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ struct cx23885_dev *h, *dev = NULL;
+ struct cx23885_fh *fh;
+ struct list_head *list;
+ enum v4l2_buf_type type = 0;
+ int radio = 0;
+
+ list_for_each(list, &cx23885_devlist) {
+ h = list_entry(list, struct cx23885_dev, devlist);
+ if (h->video_dev->minor == minor) {
+ dev = h;
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ }
+ if (h->vbi_dev &&
+ h->vbi_dev->minor == minor) {
+ dev = h;
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ }
+ if (h->radio_dev &&
+ h->radio_dev->minor == minor) {
+ radio = 1;
+ dev = h;
+ }
+ }
+ if (NULL == dev)
+ return -ENODEV;
+
+ dprintk(1, "open minor=%d radio=%d type=%s\n",
+ minor, radio, v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = type;
+ fh->width = 320;
+ fh->height = 240;
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+
+ videobuf_queue_pci_init(&fh->vidq, &cx23885_video_qops,
+ dev->pci, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx23885_buffer),
+ fh);
+
+ dprintk(1, "post videobuf_queue_init()\n");
+
+
+ return 0;
+}
+
+static ssize_t video_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (res_locked(fh->dev, RESOURCE_VIDEO))
+ return -EBUSY;
+ return videobuf_read_one(&fh->vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (!res_get(fh->dev, fh, RESOURCE_VBI))
+ return -EBUSY;
+ return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static unsigned int video_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_buffer *buf;
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if (!res_get(fh->dev, fh, RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(file, &fh->vbiq, wait);
+ }
+
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ /* streaming capture */
+ if (list_empty(&fh->vidq.stream))
+ return POLLERR;
+ buf = list_entry(fh->vidq.stream.next,
+ struct cx23885_buffer, vb.stream);
+ } else {
+ /* read() capture */
+ buf = (struct cx23885_buffer *)fh->vidq.read_buf;
+ if (NULL == buf)
+ return POLLERR;
+ }
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ return POLLIN|POLLRDNORM;
+ return 0;
+}
+
+static int video_release(struct inode *inode, struct file *file)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ /* turn off overlay */
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ /* FIXME */
+ res_free(dev, fh, RESOURCE_OVERLAY);
+ }
+
+ /* stop video capture */
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ videobuf_queue_cancel(&fh->vidq);
+ res_free(dev, fh, RESOURCE_VIDEO);
+ }
+ if (fh->vidq.read_buf) {
+ buffer_release(&fh->vidq, fh->vidq.read_buf);
+ kfree(fh->vidq.read_buf);
+ }
+
+ /* stop vbi capture */
+ if (res_check(fh, RESOURCE_VBI)) {
+ if (fh->vbiq.streaming)
+ videobuf_streamoff(&fh->vbiq);
+ if (fh->vbiq.reading)
+ videobuf_read_stop(&fh->vbiq);
+ res_free(dev, fh, RESOURCE_VBI);
+ }
+
+ videobuf_mmap_free(&fh->vidq);
+ file->private_data = NULL;
+ kfree(fh);
+
+ /* We are not putting the tuner to sleep here on exit, because
+ * we want to use the mpeg encoder in another session to capture
+ * tuner video. Closing this will result in no video to the encoder.
+ */
+
+ return 0;
+}
+
+static int video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(get_queue(fh), vma);
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO CTRL IOCTLS */
+
+int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl)
+{
+ dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __FUNCTION__);
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_CTRL, ctl);
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_get_control);
+
+int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl)
+{
+ dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)"
+ " (disabled - no action)\n", __FUNCTION__);
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_set_control);
+
+static void init_controls(struct cx23885_dev *dev)
+{
+ struct v4l2_control ctrl;
+ int i;
+
+ for (i = 0; i < CX23885_CTLS; i++) {
+ ctrl.id = cx23885_ctls[i].v.id;
+ ctrl.value = cx23885_ctls[i].v.default_value;
+
+ cx23885_set_control(dev, &ctrl);
+ }
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO IOCTLS */
+
+static int vidioc_g_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ struct cx23885_fmt *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = norm_maxw(dev->tvnorm);
+ maxh = norm_maxh(dev->tvnorm);
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ f->fmt.pix.width &= ~0x03;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ int err;
+
+ dprintk(2, "%s()\n", __FUNCTION__);
+ err = vidioc_try_fmt_cap(file, priv, f);
+
+ if (0 != err)
+ return err;
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->vidq.field = f->fmt.pix.field;
+ dprintk(2, "%s() width=%d height=%d field=%d\n", __FUNCTION__,
+ fh->width, fh->height, fh->vidq.field);
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_FMT, f);
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ strcpy(cap->driver, "cx23885");
+ strlcpy(cap->card, cx23885_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
+ cap->version = CX23885_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_VBI_CAPTURE;
+ if (UNSET != dev->tuner_type)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int vidioc_enum_fmt_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(formats)))
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int vidiocgmbuf(struct file *file, void *priv,
+ struct video_mbuf *mbuf)
+{
+ struct cx23885_fh *fh = priv;
+ struct videobuf_queue *q;
+ struct v4l2_requestbuffers req;
+ unsigned int i;
+ int err;
+
+ q = get_queue(fh);
+ memset(&req, 0, sizeof(req));
+ req.type = q->type;
+ req.count = 8;
+ req.memory = V4L2_MEMORY_MMAP;
+ err = videobuf_reqbufs(q, &req);
+ if (err < 0)
+ return err;
+
+ mbuf->frames = req.count;
+ mbuf->size = 0;
+ for (i = 0; i < mbuf->frames; i++) {
+ mbuf->offsets[i] = q->bufs[i]->boff;
+ mbuf->size += q->bufs[i]->bsize;
+ }
+ return 0;
+}
+#endif
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct cx23885_fh *fh = priv;
+ return (videobuf_reqbufs(get_queue(fh), p));
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return (videobuf_querybuf(get_queue(fh), p));
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return (videobuf_qbuf(get_queue(fh), p));
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return (videobuf_dqbuf(get_queue(fh), p,
+ file->f_flags & O_NONBLOCK));
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+ return -EINVAL;
+ if (unlikely(i != fh->type))
+ return -EINVAL;
+
+ if (unlikely(!res_get(dev, fh, get_resource(fh))))
+ return -EBUSY;
+ return videobuf_streamon(get_queue(fh));
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ int err, res;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (i != fh->type)
+ return -EINVAL;
+
+ res = get_resource(fh);
+ err = videobuf_streamoff(get_queue(fh));
+ if (err < 0)
+ return err;
+ res_free(dev, fh, res);
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ mutex_lock(&dev->lock);
+ cx23885_set_tvnorm(dev, *tvnorms);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
+{
+ static const char *iname[] = {
+ [CX23885_VMUX_COMPOSITE1] = "Composite1",
+ [CX23885_VMUX_COMPOSITE2] = "Composite2",
+ [CX23885_VMUX_COMPOSITE3] = "Composite3",
+ [CX23885_VMUX_COMPOSITE4] = "Composite4",
+ [CX23885_VMUX_SVIDEO] = "S-Video",
+ [CX23885_VMUX_TELEVISION] = "Television",
+ [CX23885_VMUX_CABLE] = "Cable TV",
+ [CX23885_VMUX_DVB] = "DVB",
+ [CX23885_VMUX_DEBUG] = "for debug only",
+ };
+ unsigned int n;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ n = i->index;
+ if (n >= 4)
+ return -EINVAL;
+
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ memset(i, 0, sizeof(*i));
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, iname[INPUT(n)->type]);
+ if ((CX23885_VMUX_TELEVISION == INPUT(n)->type) ||
+ (CX23885_VMUX_CABLE == INPUT(n)->type))
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->std = CX23885_NORMS;
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_enum_input);
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __FUNCTION__);
+ return cx23885_enum_input(dev, i);
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ *i = dev->input;
+ dprintk(1, "%s() returns %d\n", __FUNCTION__, *i);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ dprintk(1, "%s(%d)\n", __FUNCTION__, i);
+
+ if (i >= 4) {
+ dprintk(1, "%s() -EINVAL\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->lock);
+ cx23885_video_mux(dev, i);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl)
+{
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (unlikely(qctrl->id == 0))
+ return -EINVAL;
+ return cx23885_ctrl_query(qctrl);
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_get_control(dev, ctl);
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_set_control(dev, ctl);
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+ t->signal = 0xffff ; /* LOCKED */
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+
+ /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->freq;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_FREQUENCY, f);
+
+ return 0;
+}
+
+int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f)
+{
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+ dev->freq = f->frequency;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, f);
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep(10);
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_set_freq);
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
+ return -EINVAL;
+
+ return
+ cx23885_set_freq(dev, f);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *fh,
+ struct v4l2_register *reg)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_G_REGISTER, reg);
+
+ return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *fh,
+ struct v4l2_register *reg)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_S_REGISTER, reg);
+
+ return 0;
+}
+#endif
+
+/* ----------------------------------------------------------- */
+
+static void cx23885_vid_timeout(unsigned long data)
+{
+ struct cx23885_dev *dev = (struct cx23885_dev *)data;
+ struct cx23885_dmaqueue *q = &dev->vidq;
+ struct cx23885_buffer *buf;
+ unsigned long flags;
+
+ cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH01]);
+
+ cx_clear(VID_A_DMA_CTL, 0x11);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next,
+ struct cx23885_buffer, vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ printk(KERN_ERR "%s/0: [%p/%d] timeout - dma=0x%08lx\n",
+ dev->name, buf, buf->vb.i,
+ (unsigned long)buf->risc.dma);
+ }
+ cx23885_restart_video_queue(dev, q);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
+{
+ u32 mask, count;
+ int handled = 0;
+
+ mask = cx_read(VID_A_INT_MSK);
+ if (0 == (status & mask))
+ return handled;
+ cx_write(VID_A_INT_STAT, status);
+
+ dprintk(2, "%s() status = 0x%08x\n", __FUNCTION__, status);
+ /* risc op code error */
+ if (status & (1 << 16)) {
+ printk(KERN_WARNING "%s/0: video risc op code error\n",
+ dev->name);
+ cx_clear(VID_A_DMA_CTL, 0x11);
+ cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH01]);
+ }
+
+ /* risc1 y */
+ if (status & 0x01) {
+ spin_lock(&dev->slock);
+ count = cx_read(VID_A_GPCNT);
+ cx23885_video_wakeup(dev, &dev->vidq, count);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+ /* risc2 y */
+ if (status & 0x10) {
+ dprintk(2, "stopper video\n");
+ spin_lock(&dev->slock);
+ cx23885_restart_video_queue(dev, &dev->vidq);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+
+ return handled;
+}
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+static const struct file_operations video_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = video_mmap,
+ .ioctl = video_ioctl2,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device cx23885_vbi_template;
+static struct video_device cx23885_video_template = {
+ .name = "cx23885-video",
+ .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES,
+ .fops = &video_fops,
+ .minor = -1,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap,
+ .vidioc_g_fmt_cap = vidioc_g_fmt_cap,
+ .vidioc_try_fmt_cap = vidioc_try_fmt_cap,
+ .vidioc_s_fmt_cap = vidioc_s_fmt_cap,
+ .vidioc_g_fmt_vbi = cx23885_vbi_fmt,
+ .vidioc_try_fmt_vbi = cx23885_vbi_fmt,
+ .vidioc_s_fmt_vbi = cx23885_vbi_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ .vidiocgmbuf = vidiocgmbuf,
+#endif
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+ .tvnorms = CX23885_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static const struct file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .ioctl = video_ioctl2,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+
+void cx23885_video_unregister(struct cx23885_dev *dev)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ cx_clear(PCI_INT_MSK, 1);
+
+ if (dev->video_dev) {
+ if (-1 != dev->video_dev->minor)
+ video_unregister_device(dev->video_dev);
+ else
+ video_device_release(dev->video_dev);
+ dev->video_dev = NULL;
+
+ btcx_riscmem_free(dev->pci, &dev->vidq.stopper);
+ }
+}
+
+int cx23885_video_register(struct cx23885_dev *dev)
+{
+ int err;
+
+ dprintk(1, "%s()\n", __FUNCTION__);
+ spin_lock_init(&dev->slock);
+
+ /* Initialize VBI template */
+ memcpy(&cx23885_vbi_template, &cx23885_video_template,
+ sizeof(cx23885_vbi_template));
+ strcpy(cx23885_vbi_template.name, "cx23885-vbi");
+ cx23885_vbi_template.type = VID_TYPE_TELETEXT|VID_TYPE_TUNER;
+
+ dev->tvnorm = cx23885_video_template.current_norm;
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+ dev->vidq.timeout.function = cx23885_vid_timeout;
+ dev->vidq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vidq.timeout);
+ cx23885_risc_stopper(dev->pci, &dev->vidq.stopper,
+ VID_A_DMA_CTL, 0x11, 0x00);
+
+ /* Don't enable VBI yet */
+ cx_set(PCI_INT_MSK, 1);
+
+
+ /* register v4l devices */
+ dev->video_dev = cx23885_vdev_init(dev, dev->pci,
+ &cx23885_video_template, "video");
+ err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER,
+ video_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s/0: registered device video%d [v4l2]\n",
+ dev->name, dev->video_dev->minor & 0x1f);
+ /* initial device configuration */
+ mutex_lock(&dev->lock);
+ cx23885_set_tvnorm(dev, dev->tvnorm);
+ init_controls(dev);
+ cx23885_video_mux(dev, 0);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+
+fail_unreg:
+ cx23885_video_unregister(dev);
+ return err;
+}
+
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index dec4dc2fcbb4..7cb2179f2622 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -44,6 +44,10 @@
/* Max number of inputs by card */
#define MAX_CX23885_INPUT 8
+#define INPUT(nr) (&cx23885_boards[dev->board].input[nr])
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO 2
+#define RESOURCE_VBI 4
#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */
@@ -53,6 +57,62 @@
#define CX23885_BOARD_HAUPPAUGE_HVR1800 2
#define CX23885_BOARD_HAUPPAUGE_HVR1250 3
#define CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP 4
+#define CX23885_BOARD_HAUPPAUGE_HVR1500Q 5
+#define CX23885_BOARD_HAUPPAUGE_HVR1500 6
+
+/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
+#define CX23885_NORMS (\
+ V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443 | \
+ V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \
+ V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \
+ V4L2_STD_PAL_60 | V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK)
+
+struct cx23885_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+ int flags;
+ u32 cxformat;
+};
+
+struct cx23885_ctrl {
+ struct v4l2_queryctrl v;
+ u32 off;
+ u32 reg;
+ u32 mask;
+ u32 shift;
+};
+
+struct cx23885_tvnorm {
+ char *name;
+ v4l2_std_id id;
+ u32 cxiformat;
+ u32 cxoformat;
+};
+
+struct cx23885_fh {
+ struct cx23885_dev *dev;
+ enum v4l2_buf_type type;
+ int radio;
+ u32 resources;
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip *clips;
+ unsigned int nclips;
+
+ /* video capture */
+ struct cx23885_fmt *fmt;
+ unsigned int width, height;
+
+ /* vbi capture */
+ struct videobuf_queue vidq;
+ struct videobuf_queue vbiq;
+
+ /* MPEG Encoder specifics ONLY */
+ struct videobuf_queue mpegq;
+ atomic_t v4l_reading;
+};
enum cx23885_itype {
CX23885_VMUX_COMPOSITE1 = 1,
@@ -92,12 +152,28 @@ struct cx23885_input {
typedef enum {
CX23885_MPEG_UNDEFINED = 0,
- CX23885_MPEG_DVB
+ CX23885_MPEG_DVB,
+ CX23885_ANALOG_VIDEO,
} port_t;
struct cx23885_board {
char *name;
- port_t portb, portc;
+ port_t porta, portb, portc;
+ unsigned int tuner_type;
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
+
+ /* Vendors can and do run the PCIe bridge at different
+ * clock rates, driven physically by crystals on the PCBs.
+ * The core has to accomodate this. This allows the user
+ * to add new boards with new frequencys. The value is
+ * expressed in Hz.
+ *
+ * The core framework will default this value based on
+ * current designs, but it can vary.
+ */
+ u32 clk_freq;
struct cx23885_input input[MAX_CX23885_INPUT];
};
@@ -189,6 +265,11 @@ struct cx23885_dev {
u32 __iomem *lmmio;
u8 __iomem *bmmio;
int pci_irqmask;
+ int hwrevision;
+
+ /* This valud is board specific and is used to configure the
+ * AV core so we see nice clean and stable video and audio. */
+ u32 clk_freq;
/* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
struct cx23885_i2c i2c_bus[3];
@@ -210,8 +291,31 @@ struct cx23885_dev {
CX23885_BRIDGE_885 = 885,
CX23885_BRIDGE_887 = 887,
} bridge;
+
+ /* Analog video */
+ u32 resources;
+ unsigned int input;
+ u32 tvaudio;
+ v4l2_std_id tvnorm;
+ unsigned int tuner_type;
+ unsigned char tuner_addr;
+ unsigned int radio_type;
+ unsigned char radio_addr;
+ unsigned int has_radio;
+
+ /* V4l */
+ u32 freq;
+ struct video_device *video_dev;
+ struct video_device *vbi_dev;
+ struct video_device *radio_dev;
+
+ struct cx23885_dmaqueue vidq;
+ struct cx23885_dmaqueue vbiq;
+ spinlock_t slock;
};
+extern struct list_head cx23885_devlist;
+
#define SRAM_CH01 0 /* Video A */
#define SRAM_CH02 1 /* VBI A */
#define SRAM_CH03 2 /* Video B */
@@ -254,19 +358,42 @@ struct sram_channel {
#define cx_set(reg,bit) cx_andor((reg),(bit),(bit))
#define cx_clear(reg,bit) cx_andor((reg),(bit),0)
+/* ----------------------------------------------------------- */
+/* cx23885-core.c */
+
extern int cx23885_sram_channel_setup(struct cx23885_dev *dev,
struct sram_channel *ch,
unsigned int bpl, u32 risc);
-/* ----------------------------------------------------------- */
-/* cx23885-cards.c */
+extern void cx23885_sram_channel_dump(struct cx23885_dev *dev,
+ struct sram_channel *ch);
+extern int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value);
+
+extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines);
+
+void cx23885_cancel_buffers(struct cx23885_tsport *port);
+
+extern int cx23885_restart_queue(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q);
+
+extern void cx23885_wakeup(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q, u32 count);
+
+
+/* ----------------------------------------------------------- */
+/* cx23885-cards.c */
extern struct cx23885_board cx23885_boards[];
extern const unsigned int cx23885_bcount;
extern struct cx23885_subid cx23885_subids[];
extern const unsigned int cx23885_idcount;
+extern int cx23885_tuner_callback(void *priv, int command, int arg);
extern void cx23885_card_list(struct cx23885_dev *dev);
extern int cx23885_ir_init(struct cx23885_dev *dev);
extern void cx23885_gpio_setup(struct cx23885_dev *dev);
@@ -280,19 +407,50 @@ extern int cx23885_buf_prepare(struct videobuf_queue *q,
struct cx23885_tsport *port,
struct cx23885_buffer *buf,
enum v4l2_field field);
-
extern void cx23885_buf_queue(struct cx23885_tsport *port,
struct cx23885_buffer *buf);
extern void cx23885_free_buffer(struct videobuf_queue *q,
struct cx23885_buffer *buf);
/* ----------------------------------------------------------- */
+/* cx23885-video.c */
+/* Video */
+extern int cx23885_video_register(struct cx23885_dev *dev);
+extern void cx23885_video_unregister(struct cx23885_dev *dev);
+extern int cx23885_video_irq(struct cx23885_dev *dev, u32 status);
+
+/* ----------------------------------------------------------- */
+/* cx23885-vbi.c */
+extern int cx23885_vbi_fmt(struct file *file, void *priv,
+ struct v4l2_format *f);
+extern void cx23885_vbi_timeout(unsigned long data);
+extern struct videobuf_queue_ops cx23885_vbi_qops;
+
/* cx23885-i2c.c */
extern int cx23885_i2c_register(struct cx23885_i2c *bus);
extern int cx23885_i2c_unregister(struct cx23885_i2c *bus);
extern void cx23885_call_i2c_clients(struct cx23885_i2c *bus, unsigned int cmd,
void *arg);
+/* ----------------------------------------------------------- */
+/* tv norms */
+
+static inline unsigned int norm_maxw(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
+}
+
+static inline unsigned int norm_maxh(v4l2_std_id norm)
+{
+ return (norm & V4L2_STD_625_50) ? 576 : 480;
+}
+
+static inline unsigned int norm_swidth(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
+}
+
+
/*
* Local variables:
* c-basic-offset: 8
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
index 3d46a776df36..d6421e1e8f6a 100644
--- a/drivers/media/video/cx25840/cx25840-audio.c
+++ b/drivers/media/video/cx25840/cx25840-audio.c
@@ -32,118 +32,156 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
/* common for all inputs and rates */
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
- cx25840_write(client, 0x127, 0x50);
+ if (!state->is_cx23885)
+ cx25840_write(client, 0x127, 0x50);
if (state->aud_input != CX25840_AUDIO_SERIAL) {
switch (freq) {
case 32000:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
/* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x0f040610);
+ cx25840_write4(client, 0x108, 0x1006040f);
/* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0xee39bb01);
+ cx25840_write4(client, 0x110, 0x01bb39ee);
if (state->is_cx25836)
break;
/* src3/4/6_ctl = 0x0801f77f */
- cx25840_write4(client, 0x900, 0x7ff70108);
- cx25840_write4(client, 0x904, 0x7ff70108);
- cx25840_write4(client, 0x90c, 0x7ff70108);
+ cx25840_write4(client, 0x900, 0x0801f77f);
+ cx25840_write4(client, 0x904, 0x0801f77f);
+ cx25840_write4(client, 0x90c, 0x0801f77f);
break;
case 44100:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
/* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x0f040910);
+ cx25840_write4(client, 0x108, 0x1009040f);
/* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0xd66bec00);
+ cx25840_write4(client, 0x110, 0x00ec6bd6);
if (state->is_cx25836)
break;
/* src3/4/6_ctl = 0x08016d59 */
- cx25840_write4(client, 0x900, 0x596d0108);
- cx25840_write4(client, 0x904, 0x596d0108);
- cx25840_write4(client, 0x90c, 0x596d0108);
+ cx25840_write4(client, 0x900, 0x08016d59);
+ cx25840_write4(client, 0x904, 0x08016d59);
+ cx25840_write4(client, 0x90c, 0x08016d59);
break;
case 48000:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
/* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x0f040a10);
+ cx25840_write4(client, 0x108, 0x100a040f);
/* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0xe5d69800);
+ cx25840_write4(client, 0x110, 0x0098d6e5);
if (state->is_cx25836)
break;
/* src3/4/6_ctl = 0x08014faa */
- cx25840_write4(client, 0x900, 0xaa4f0108);
- cx25840_write4(client, 0x904, 0xaa4f0108);
- cx25840_write4(client, 0x90c, 0xaa4f0108);
+ cx25840_write4(client, 0x900, 0x08014faa);
+ cx25840_write4(client, 0x904, 0x08014faa);
+ cx25840_write4(client, 0x90c, 0x08014faa);
break;
}
} else {
switch (freq) {
case 32000:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
/* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x0f04081e);
+ cx25840_write4(client, 0x108, 0x1e08040f);
/* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0x69082a01);
+ cx25840_write4(client, 0x110, 0x012a0869);
if (state->is_cx25836)
break;
/* src1_ctl = 0x08010000 */
- cx25840_write4(client, 0x8f8, 0x00000108);
+ cx25840_write4(client, 0x8f8, 0x08010000);
/* src3/4/6_ctl = 0x08020000 */
- cx25840_write4(client, 0x900, 0x00000208);
- cx25840_write4(client, 0x904, 0x00000208);
- cx25840_write4(client, 0x90c, 0x00000208);
+ cx25840_write4(client, 0x900, 0x08020000);
+ cx25840_write4(client, 0x904, 0x08020000);
+ cx25840_write4(client, 0x90c, 0x08020000);
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
cx25840_write(client, 0x127, 0x54);
break;
case 44100:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
+
/* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x0f040918);
+ cx25840_write4(client, 0x108, 0x1809040f);
/* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0xd66bec00);
+ cx25840_write4(client, 0x110, 0x00ec6bd6);
if (state->is_cx25836)
break;
/* src1_ctl = 0x08010000 */
- cx25840_write4(client, 0x8f8, 0xcd600108);
+ cx25840_write4(client, 0x8f8, 0x080160cd);
/* src3/4/6_ctl = 0x08020000 */
- cx25840_write4(client, 0x900, 0x85730108);
- cx25840_write4(client, 0x904, 0x85730108);
- cx25840_write4(client, 0x90c, 0x85730108);
+ cx25840_write4(client, 0x900, 0x08017385);
+ cx25840_write4(client, 0x904, 0x08017385);
+ cx25840_write4(client, 0x90c, 0x08017385);
break;
case 48000:
- /* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x0f040a18);
+ if (!state->is_cx23885) {
+ /* VID_PLL and AUX_PLL */
+ cx25840_write4(client, 0x108, 0x180a040f);
- /* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0xe5d69800);
+ /* AUX_PLL_FRAC */
+ cx25840_write4(client, 0x110, 0x0098d6e5);
+ }
if (state->is_cx25836)
break;
- /* src1_ctl = 0x08010000 */
- cx25840_write4(client, 0x8f8, 0x00800108);
+ if (!state->is_cx23885) {
+ /* src1_ctl */
+ cx25840_write4(client, 0x8f8, 0x08018000);
- /* src3/4/6_ctl = 0x08020000 */
- cx25840_write4(client, 0x900, 0x55550108);
- cx25840_write4(client, 0x904, 0x55550108);
- cx25840_write4(client, 0x90c, 0x55550108);
+ /* src3/4/6_ctl */
+ cx25840_write4(client, 0x900, 0x08015555);
+ cx25840_write4(client, 0x904, 0x08015555);
+ cx25840_write4(client, 0x90c, 0x08015555);
+ } else {
+
+ cx25840_write4(client, 0x8f8, 0x0801867c);
+
+ cx25840_write4(client, 0x900, 0x08014faa);
+ cx25840_write4(client, 0x904, 0x08014faa);
+ cx25840_write4(client, 0x90c, 0x08014faa);
+ }
break;
}
}
@@ -168,14 +206,14 @@ void cx25840_audio_set_path(struct i2c_client *client)
if (state->aud_input == CX25840_AUDIO_SERIAL) {
/* Set Path1 to Serial Audio Input */
- cx25840_write4(client, 0x8d0, 0x12100101);
+ cx25840_write4(client, 0x8d0, 0x01011012);
/* The microcontroller should not be started for the
* non-tuner inputs: autodetection is specific for
* TV audio. */
} else {
/* Set Path1 to Analog Demod Main Channel */
- cx25840_write4(client, 0x8d0, 0x7038061f);
+ cx25840_write4(client, 0x8d0, 0x1f063870);
}
set_audclk_freq(client, state->audclk_freq);
@@ -188,6 +226,11 @@ void cx25840_audio_set_path(struct i2c_client *client)
/* deassert soft reset */
cx25840_and_or(client, 0x810, ~0x1, 0x00);
+
+ if (state->is_cx23885) {
+ /* Ensure the controller is running when we exit */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+ }
}
static int get_volume(struct i2c_client *client)
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 15f191e170d2..756a1eeb274e 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -13,6 +13,8 @@
* NTSC sliced VBI support by Christopher Neufeld <television@cneufeld.ca>
* with additional fixes by Hans Verkuil <hverkuil@xs4all.nl>.
*
+ * CX23885 support by 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
@@ -37,6 +39,7 @@
#include <linux/delay.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include <media/cx25840.h>
#include "cx25840-core.h"
@@ -72,10 +75,10 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value)
u8 buffer[6];
buffer[0] = addr >> 8;
buffer[1] = addr & 0xff;
- buffer[2] = value >> 24;
- buffer[3] = (value >> 16) & 0xff;
- buffer[4] = (value >> 8) & 0xff;
- buffer[5] = value & 0xff;
+ buffer[2] = value & 0xff;
+ buffer[3] = (value >> 8) & 0xff;
+ buffer[4] = (value >> 16) & 0xff;
+ buffer[5] = value >> 24;
return i2c_master_send(client, buffer, 6);
}
@@ -122,8 +125,6 @@ int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
enum cx25840_audio_input aud_input);
-static void log_audio_status(struct i2c_client *client);
-static void log_video_status(struct i2c_client *client);
/* ----------------------------------------------------------------------- */
@@ -256,6 +257,96 @@ static void cx25840_initialize(struct i2c_client *client)
cx25840_and_or(client, 0x803, ~0x10, 0x10);
}
+static void cx23885_initialize(struct i2c_client *client)
+{
+ DEFINE_WAIT(wait);
+ struct cx25840_state *state = i2c_get_clientdata(client);
+ struct workqueue_struct *q;
+
+ /* Internal Reset */
+ cx25840_and_or(client, 0x102, ~0x01, 0x01);
+ cx25840_and_or(client, 0x102, ~0x01, 0x00);
+
+ /* Stop microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x00);
+
+ /* DIF in reset? */
+ cx25840_write(client, 0x398, 0);
+
+ /* Trust the default xtal, no division */
+ /* This changes for the cx23888 products */
+ cx25840_write(client, 0x2, 0x76);
+
+ /* Bring down the regulator for AUX clk */
+ cx25840_write(client, 0x1, 0x40);
+
+ /* Sys PLL frac */
+ cx25840_write4(client, 0x11c, 0x01d1744c);
+
+ /* Sys PLL int */
+ cx25840_write4(client, 0x118, 0x00000416);
+
+ /* Disable DIF bypass */
+ cx25840_write4(client, 0x33c, 0x00000001);
+
+ /* DIF Src phase inc */
+ cx25840_write4(client, 0x340, 0x0df7df83);
+
+ /* Vid PLL frac */
+ cx25840_write4(client, 0x10c, 0x01b6db7b);
+
+ /* Vid PLL int */
+ cx25840_write4(client, 0x108, 0x00000512);
+
+ /* Luma */
+ cx25840_write4(client, 0x414, 0x00107d12);
+
+ /* Chroma */
+ cx25840_write4(client, 0x420, 0x3d008282);
+
+ /* Aux PLL frac */
+ cx25840_write4(client, 0x114, 0x017dbf48);
+
+ /* Aux PLL int */
+ cx25840_write4(client, 0x110, 0x000a030e);
+
+ /* ADC2 input select */
+ cx25840_write(client, 0x102, 0x10);
+
+ /* VIN1 & VIN5 */
+ cx25840_write(client, 0x103, 0x11);
+
+ /* Enable format auto detect */
+ cx25840_write(client, 0x400, 0);
+ /* Fast subchroma lock */
+ /* White crush, Chroma AGC & Chroma Killer enabled */
+ cx25840_write(client, 0x401, 0xe8);
+
+ /* Select AFE clock pad output source */
+ cx25840_write(client, 0x144, 0x05);
+
+ /* Do the firmware load in a work handler to prevent.
+ Otherwise the kernel is blocked waiting for the
+ bit-banging i2c interface to finish uploading the
+ firmware. */
+ INIT_WORK(&state->fw_work, cx25840_work_handler);
+ init_waitqueue_head(&state->fw_wait);
+ q = create_singlethread_workqueue("cx25840_fw");
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
+ queue_work(q, &state->fw_work);
+ schedule();
+ finish_wait(&state->fw_wait, &wait);
+ destroy_workqueue(q);
+
+ cx25840_vbi_setup(client);
+
+ /* (re)set input */
+ set_input(client, state->vid_input, state->aud_input);
+
+ /* start microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+}
+
/* ----------------------------------------------------------------------- */
static void input_change(struct i2c_client *client)
@@ -319,9 +410,22 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
vid_input <= CX25840_COMPOSITE8);
u8 reg;
- v4l_dbg(1, cx25840_debug, client, "decoder set video input %d, audio input %d\n",
- vid_input, aud_input);
+ v4l_dbg(1, cx25840_debug, client,
+ "decoder set video input %d, audio input %d\n",
+ vid_input, aud_input);
+
+ if (vid_input >= CX25840_VIN1_CH1) {
+ v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n",
+ vid_input);
+ reg = vid_input & 0xff;
+ if ((vid_input & CX25840_SVIDEO_ON) == CX25840_SVIDEO_ON)
+ is_composite = 0;
+ else
+ is_composite = 1;
+ v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n",
+ reg, is_composite);
+ } else
if (is_composite) {
reg = 0xf0 + (vid_input - CX25840_COMPOSITE1);
} else {
@@ -331,7 +435,8 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
if ((vid_input & ~0xff0) ||
luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA4 ||
chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) {
- v4l_err(client, "0x%04x is not a valid video input!\n", vid_input);
+ v4l_err(client, "0x%04x is not a valid video input!\n",
+ vid_input);
return -EINVAL;
}
reg = 0xf0 + ((luma - CX25840_SVIDEO_LUMA1) >> 4);
@@ -344,31 +449,49 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
}
}
- switch (aud_input) {
- case CX25840_AUDIO_SERIAL:
- /* do nothing, use serial audio input */
- break;
- case CX25840_AUDIO4: reg &= ~0x30; break;
- case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break;
- case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break;
- case CX25840_AUDIO7: reg &= ~0xc0; break;
- case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break;
+ /* The caller has previously prepared the correct routing
+ * configuration in reg (for the cx23885) so we have no
+ * need to attempt to flip bits for earlier av decoders.
+ */
+ if (!state->is_cx23885) {
+ switch (aud_input) {
+ case CX25840_AUDIO_SERIAL:
+ /* do nothing, use serial audio input */
+ break;
+ case CX25840_AUDIO4: reg &= ~0x30; break;
+ case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break;
+ case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break;
+ case CX25840_AUDIO7: reg &= ~0xc0; break;
+ case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break;
- default:
- v4l_err(client, "0x%04x is not a valid audio input!\n", aud_input);
- return -EINVAL;
+ default:
+ v4l_err(client, "0x%04x is not a valid audio input!\n",
+ aud_input);
+ return -EINVAL;
+ }
}
cx25840_write(client, 0x103, reg);
+
/* Set INPUT_MODE to Composite (0) or S-Video (1) */
cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02);
- /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
- cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
- /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
- if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
- cx25840_and_or(client, 0x102, ~0x4, 4);
- else
- cx25840_and_or(client, 0x102, ~0x4, 0);
+
+ if (!state->is_cx23885) {
+ /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
+ cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
+ /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */
+ if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
+ cx25840_and_or(client, 0x102, ~0x4, 4);
+ else
+ cx25840_and_or(client, 0x102, ~0x4, 0);
+ } else {
+ if (is_composite)
+ /* ADC2 input select channel 2 */
+ cx25840_and_or(client, 0x102, ~0x2, 0);
+ else
+ /* ADC2 input select channel 3 */
+ cx25840_and_or(client, 0x102, ~0x2, 2);
+ }
state->vid_input = vid_input;
state->aud_input = aud_input;
@@ -376,6 +499,25 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
cx25840_audio_set_path(client);
input_change(client);
}
+
+ if (state->is_cx23885) {
+ /* Audio channel 1 src : Parallel 1 */
+ cx25840_write(client, 0x124, 0x03);
+
+ /* Select AFE clock pad output source */
+ cx25840_write(client, 0x144, 0x05);
+
+ /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
+ cx25840_write(client, 0x914, 0xa0);
+
+ /* I2S_OUT_CTL:
+ * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1
+ * I2S_OUT_MASTER_MODE = Master
+ */
+ cx25840_write(client, 0x918, 0xa0);
+ cx25840_write(client, 0x919, 0x01);
+ }
+
return 0;
}
@@ -641,6 +783,200 @@ static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
/* ----------------------------------------------------------------------- */
+static void log_video_status(struct i2c_client *client)
+{
+ static const char *const fmt_strs[] = {
+ "0x0",
+ "NTSC-M", "NTSC-J", "NTSC-4.43",
+ "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60",
+ "0x9", "0xA", "0xB",
+ "SECAM",
+ "0xD", "0xE", "0xF"
+ };
+
+ struct cx25840_state *state = i2c_get_clientdata(client);
+ u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf;
+ u8 gen_stat1 = cx25840_read(client, 0x40d);
+ u8 gen_stat2 = cx25840_read(client, 0x40e);
+ int vid_input = state->vid_input;
+
+ v4l_info(client, "Video signal: %spresent\n",
+ (gen_stat2 & 0x20) ? "" : "not ");
+ v4l_info(client, "Detected format: %s\n",
+ fmt_strs[gen_stat1 & 0xf]);
+
+ v4l_info(client, "Specified standard: %s\n",
+ vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection");
+
+ if (vid_input >= CX25840_COMPOSITE1 &&
+ vid_input <= CX25840_COMPOSITE8) {
+ v4l_info(client, "Specified video input: Composite %d\n",
+ vid_input - CX25840_COMPOSITE1 + 1);
+ } else {
+ v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n",
+ (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8);
+ }
+
+ v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void log_audio_status(struct i2c_client *client)
+{
+ struct cx25840_state *state = i2c_get_clientdata(client);
+ u8 download_ctl = cx25840_read(client, 0x803);
+ u8 mod_det_stat0 = cx25840_read(client, 0x804);
+ u8 mod_det_stat1 = cx25840_read(client, 0x805);
+ u8 audio_config = cx25840_read(client, 0x808);
+ u8 pref_mode = cx25840_read(client, 0x809);
+ u8 afc0 = cx25840_read(client, 0x80b);
+ u8 mute_ctl = cx25840_read(client, 0x8d3);
+ int aud_input = state->aud_input;
+ char *p;
+
+ switch (mod_det_stat0) {
+ case 0x00: p = "mono"; break;
+ case 0x01: p = "stereo"; break;
+ case 0x02: p = "dual"; break;
+ case 0x04: p = "tri"; break;
+ case 0x10: p = "mono with SAP"; break;
+ case 0x11: p = "stereo with SAP"; break;
+ case 0x12: p = "dual with SAP"; break;
+ case 0x14: p = "tri with SAP"; break;
+ case 0xfe: p = "forced mode"; break;
+ default: p = "not defined";
+ }
+ v4l_info(client, "Detected audio mode: %s\n", p);
+
+ switch (mod_det_stat1) {
+ case 0x00: p = "not defined"; break;
+ case 0x01: p = "EIAJ"; break;
+ case 0x02: p = "A2-M"; break;
+ case 0x03: p = "A2-BG"; break;
+ case 0x04: p = "A2-DK1"; break;
+ case 0x05: p = "A2-DK2"; break;
+ case 0x06: p = "A2-DK3"; break;
+ case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
+ case 0x08: p = "AM-L"; break;
+ case 0x09: p = "NICAM-BG"; break;
+ case 0x0a: p = "NICAM-DK"; break;
+ case 0x0b: p = "NICAM-I"; break;
+ case 0x0c: p = "NICAM-L"; break;
+ case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break;
+ case 0x0e: p = "IF FM Radio"; break;
+ case 0x0f: p = "BTSC"; break;
+ case 0x10: p = "high-deviation FM"; break;
+ case 0x11: p = "very high-deviation FM"; 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";
+ }
+ v4l_info(client, "Detected audio standard: %s\n", p);
+ v4l_info(client, "Audio muted: %s\n",
+ (state->unmute_volume >= 0) ? "yes" : "no");
+ v4l_info(client, "Audio microcontroller: %s\n",
+ (download_ctl & 0x10) ?
+ ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
+
+ switch (audio_config >> 4) {
+ case 0x00: p = "undefined"; break;
+ case 0x01: p = "BTSC"; break;
+ case 0x02: p = "EIAJ"; break;
+ case 0x03: p = "A2-M"; break;
+ case 0x04: p = "A2-BG"; break;
+ case 0x05: p = "A2-DK1"; break;
+ case 0x06: p = "A2-DK2"; break;
+ case 0x07: p = "A2-DK3"; break;
+ case 0x08: p = "A1 (6.0 MHz FM Mono)"; break;
+ case 0x09: p = "AM-L"; break;
+ case 0x0a: p = "NICAM-BG"; break;
+ case 0x0b: p = "NICAM-DK"; break;
+ case 0x0c: p = "NICAM-I"; break;
+ case 0x0d: p = "NICAM-L"; break;
+ case 0x0e: p = "FM radio"; break;
+ case 0x0f: p = "automatic detection"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Configured audio standard: %s\n", p);
+
+ if ((audio_config >> 4) < 0xF) {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break;
+ case 0x01: p = "MONO2 (LANGUAGE B)"; break;
+ case 0x02: p = "MONO3 (STEREO forced MONO)"; break;
+ case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break;
+ case 0x04: p = "STEREO"; break;
+ case 0x05: p = "DUAL1 (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;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Configured audio mode: %s\n", p);
+ } else {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "BG"; break;
+ case 0x01: p = "DK1"; break;
+ case 0x02: p = "DK2"; break;
+ case 0x03: p = "DK3"; break;
+ case 0x04: p = "I"; break;
+ case 0x05: p = "L"; break;
+ case 0x06: p = "BTSC"; break;
+ case 0x07: p = "EIAJ"; break;
+ case 0x08: p = "A2-M"; break;
+ case 0x09: p = "FM Radio"; break;
+ case 0x0f: p = "automatic standard and mode detection"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Configured audio system: %s\n", p);
+ }
+
+ if (aud_input) {
+ v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input);
+ } else {
+ v4l_info(client, "Specified audio input: External\n");
+ }
+
+ switch (pref_mode & 0xf) {
+ case 0: p = "mono/language A"; break;
+ case 1: p = "language B"; break;
+ case 2: p = "language C"; break;
+ case 3: p = "analog fallback"; break;
+ case 4: p = "stereo"; break;
+ case 5: p = "language AC"; break;
+ case 6: p = "language BC"; break;
+ case 7: p = "language AB"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Preferred audio mode: %s\n", p);
+
+ if ((audio_config & 0xf) == 0xf) {
+ switch ((afc0 >> 3) & 0x3) {
+ case 0: p = "system DK"; break;
+ case 1: p = "system L"; break;
+ case 2: p = "autodetect"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Selected 65 MHz format: %s\n", p);
+
+ switch (afc0 & 0x7) {
+ case 0: p = "chroma"; break;
+ case 1: p = "BTSC"; break;
+ case 2: p = "EIAJ"; break;
+ case 3: p = "A2-M"; break;
+ case 4: p = "autodetect"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Selected 45 MHz format: %s\n", p);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
static int cx25840_command(struct i2c_client *client, unsigned int cmd,
void *arg)
{
@@ -660,6 +996,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
state->is_initialized = 1;
if (state->is_cx25836)
cx25836_initialize(client);
+ else if (state->is_cx23885)
+ cx23885_initialize(client);
else
cx25840_initialize(client);
}
@@ -677,6 +1015,7 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
+
if (cmd == VIDIOC_DBG_G_REGISTER)
reg->val = cx25840_read(client, reg->reg & 0x0fff);
else
@@ -693,14 +1032,26 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
case VIDIOC_STREAMON:
v4l_dbg(1, cx25840_debug, client, "enable output\n");
- cx25840_write(client, 0x115, state->is_cx25836 ? 0x0c : 0x8c);
- cx25840_write(client, 0x116, state->is_cx25836 ? 0x04 : 0x07);
+ if (state->is_cx23885) {
+ u8 v = (cx25840_read(client, 0x421) | 0x0b);
+ cx25840_write(client, 0x421, v);
+ } else {
+ cx25840_write(client, 0x115,
+ state->is_cx25836 ? 0x0c : 0x8c);
+ cx25840_write(client, 0x116,
+ state->is_cx25836 ? 0x04 : 0x07);
+ }
break;
case VIDIOC_STREAMOFF:
v4l_dbg(1, cx25840_debug, client, "disable output\n");
- cx25840_write(client, 0x115, 0x00);
- cx25840_write(client, 0x116, 0x00);
+ if (state->is_cx23885) {
+ u8 v = cx25840_read(client, 0x421) & ~(0x0b);
+ cx25840_write(client, 0x421, v);
+ } else {
+ cx25840_write(client, 0x115, 0x00);
+ cx25840_write(client, 0x116, 0x00);
+ }
break;
case VIDIOC_LOG_STATUS:
@@ -863,6 +1214,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
case VIDIOC_INT_RESET:
if (state->is_cx25836)
cx25836_initialize(client);
+ else if (state->is_cx23885)
+ cx23885_initialize(client);
else
cx25840_initialize(client);
break;
@@ -879,35 +1232,21 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
/* ----------------------------------------------------------------------- */
-static struct i2c_driver i2c_driver_cx25840;
-
-static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
- int kind)
+static int cx25840_probe(struct i2c_client *client)
{
- struct i2c_client *client;
struct cx25840_state *state;
u32 id;
u16 device_id;
- /* Check if the adapter supports the needed features
- * Not until kernel version 2.6.11 did the bit-algo
- * correctly report that it would do an I2C-level xfer */
- if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
- return 0;
-
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == 0)
- return -ENOMEM;
-
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver_cx25840;
- snprintf(client->name, sizeof(client->name) - 1, "cx25840");
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", client->addr << 1);
device_id = cx25840_read(client, 0x101) << 8;
device_id |= cx25840_read(client, 0x100);
+ v4l_dbg(1, cx25840_debug, client, "device_id = 0x%04x\n", device_id);
/* The high byte of the device ID should be
* 0x83 for the cx2583x and 0x84 for the cx2584x */
@@ -916,16 +1255,18 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
}
else if ((device_id & 0xff00) == 0x8400) {
id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
+ } else if (device_id == 0x0000) {
+ id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
+ } else if (device_id == 0x1313) {
+ id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
}
else {
v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n");
- kfree(client);
- return 0;
+ return -ENODEV;
}
state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL);
if (state == NULL) {
- kfree(client);
return -ENOMEM;
}
@@ -939,6 +1280,7 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
i2c_set_clientdata(client, state);
state->c = client;
state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
+ state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
state->vid_input = CX25840_COMPOSITE7;
state->aud_input = CX25840_AUDIO8;
state->audclk_freq = 48000;
@@ -949,250 +1291,19 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
state->id = id;
state->rev = device_id;
- i2c_attach_client(client);
-
- return 0;
-}
-
-static int cx25840_attach_adapter(struct i2c_adapter *adapter)
-{
- if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, &cx25840_detect_client);
return 0;
}
-static int cx25840_detach_client(struct i2c_client *client)
+static int cx25840_remove(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
- int err;
-
- err = i2c_detach_client(client);
- if (err) {
- return err;
- }
-
- kfree(state);
- kfree(client);
-
+ kfree(i2c_get_clientdata(client));
return 0;
}
-/* ----------------------------------------------------------------------- */
-
-static struct i2c_driver i2c_driver_cx25840 = {
- .driver = {
- .name = "cx25840",
- },
- .id = I2C_DRIVERID_CX25840,
- .attach_adapter = cx25840_attach_adapter,
- .detach_client = cx25840_detach_client,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "cx25840",
+ .driverid = I2C_DRIVERID_CX25840,
.command = cx25840_command,
+ .probe = cx25840_probe,
+ .remove = cx25840_remove,
};
-
-
-static int __init m__init(void)
-{
- return i2c_add_driver(&i2c_driver_cx25840);
-}
-
-static void __exit m__exit(void)
-{
- i2c_del_driver(&i2c_driver_cx25840);
-}
-
-module_init(m__init);
-module_exit(m__exit);
-
-/* ----------------------------------------------------------------------- */
-
-static void log_video_status(struct i2c_client *client)
-{
- static const char *const fmt_strs[] = {
- "0x0",
- "NTSC-M", "NTSC-J", "NTSC-4.43",
- "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60",
- "0x9", "0xA", "0xB",
- "SECAM",
- "0xD", "0xE", "0xF"
- };
-
- struct cx25840_state *state = i2c_get_clientdata(client);
- u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf;
- u8 gen_stat1 = cx25840_read(client, 0x40d);
- u8 gen_stat2 = cx25840_read(client, 0x40e);
- int vid_input = state->vid_input;
-
- v4l_info(client, "Video signal: %spresent\n",
- (gen_stat2 & 0x20) ? "" : "not ");
- v4l_info(client, "Detected format: %s\n",
- fmt_strs[gen_stat1 & 0xf]);
-
- v4l_info(client, "Specified standard: %s\n",
- vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection");
-
- if (vid_input >= CX25840_COMPOSITE1 &&
- vid_input <= CX25840_COMPOSITE8) {
- v4l_info(client, "Specified video input: Composite %d\n",
- vid_input - CX25840_COMPOSITE1 + 1);
- } else {
- v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n",
- (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8);
- }
-
- v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static void log_audio_status(struct i2c_client *client)
-{
- struct cx25840_state *state = i2c_get_clientdata(client);
- u8 download_ctl = cx25840_read(client, 0x803);
- u8 mod_det_stat0 = cx25840_read(client, 0x804);
- u8 mod_det_stat1 = cx25840_read(client, 0x805);
- u8 audio_config = cx25840_read(client, 0x808);
- u8 pref_mode = cx25840_read(client, 0x809);
- u8 afc0 = cx25840_read(client, 0x80b);
- u8 mute_ctl = cx25840_read(client, 0x8d3);
- int aud_input = state->aud_input;
- char *p;
-
- switch (mod_det_stat0) {
- case 0x00: p = "mono"; break;
- case 0x01: p = "stereo"; break;
- case 0x02: p = "dual"; break;
- case 0x04: p = "tri"; break;
- case 0x10: p = "mono with SAP"; break;
- case 0x11: p = "stereo with SAP"; break;
- case 0x12: p = "dual with SAP"; break;
- case 0x14: p = "tri with SAP"; break;
- case 0xfe: p = "forced mode"; break;
- default: p = "not defined";
- }
- v4l_info(client, "Detected audio mode: %s\n", p);
-
- switch (mod_det_stat1) {
- case 0x00: p = "not defined"; break;
- case 0x01: p = "EIAJ"; break;
- case 0x02: p = "A2-M"; break;
- case 0x03: p = "A2-BG"; break;
- case 0x04: p = "A2-DK1"; break;
- case 0x05: p = "A2-DK2"; break;
- case 0x06: p = "A2-DK3"; break;
- case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
- case 0x08: p = "AM-L"; break;
- case 0x09: p = "NICAM-BG"; break;
- case 0x0a: p = "NICAM-DK"; break;
- case 0x0b: p = "NICAM-I"; break;
- case 0x0c: p = "NICAM-L"; break;
- case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break;
- case 0x0e: p = "IF FM Radio"; break;
- case 0x0f: p = "BTSC"; break;
- case 0x10: p = "high-deviation FM"; break;
- case 0x11: p = "very high-deviation FM"; 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";
- }
- v4l_info(client, "Detected audio standard: %s\n", p);
- v4l_info(client, "Audio muted: %s\n",
- (state->unmute_volume >= 0) ? "yes" : "no");
- v4l_info(client, "Audio microcontroller: %s\n",
- (download_ctl & 0x10) ?
- ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
-
- switch (audio_config >> 4) {
- case 0x00: p = "undefined"; break;
- case 0x01: p = "BTSC"; break;
- case 0x02: p = "EIAJ"; break;
- case 0x03: p = "A2-M"; break;
- case 0x04: p = "A2-BG"; break;
- case 0x05: p = "A2-DK1"; break;
- case 0x06: p = "A2-DK2"; break;
- case 0x07: p = "A2-DK3"; break;
- case 0x08: p = "A1 (6.0 MHz FM Mono)"; break;
- case 0x09: p = "AM-L"; break;
- case 0x0a: p = "NICAM-BG"; break;
- case 0x0b: p = "NICAM-DK"; break;
- case 0x0c: p = "NICAM-I"; break;
- case 0x0d: p = "NICAM-L"; break;
- case 0x0e: p = "FM radio"; break;
- case 0x0f: p = "automatic detection"; break;
- default: p = "undefined";
- }
- v4l_info(client, "Configured audio standard: %s\n", p);
-
- if ((audio_config >> 4) < 0xF) {
- switch (audio_config & 0xF) {
- case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break;
- case 0x01: p = "MONO2 (LANGUAGE B)"; break;
- case 0x02: p = "MONO3 (STEREO forced MONO)"; break;
- case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break;
- case 0x04: p = "STEREO"; break;
- case 0x05: p = "DUAL1 (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;
- default: p = "undefined";
- }
- v4l_info(client, "Configured audio mode: %s\n", p);
- } else {
- switch (audio_config & 0xF) {
- case 0x00: p = "BG"; break;
- case 0x01: p = "DK1"; break;
- case 0x02: p = "DK2"; break;
- case 0x03: p = "DK3"; break;
- case 0x04: p = "I"; break;
- case 0x05: p = "L"; break;
- case 0x06: p = "BTSC"; break;
- case 0x07: p = "EIAJ"; break;
- case 0x08: p = "A2-M"; break;
- case 0x09: p = "FM Radio"; break;
- case 0x0f: p = "automatic standard and mode detection"; break;
- default: p = "undefined";
- }
- v4l_info(client, "Configured audio system: %s\n", p);
- }
-
- if (aud_input) {
- v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input);
- } else {
- v4l_info(client, "Specified audio input: External\n");
- }
-
- switch (pref_mode & 0xf) {
- case 0: p = "mono/language A"; break;
- case 1: p = "language B"; break;
- case 2: p = "language C"; break;
- case 3: p = "analog fallback"; break;
- case 4: p = "stereo"; break;
- case 5: p = "language AC"; break;
- case 6: p = "language BC"; break;
- case 7: p = "language AB"; break;
- default: p = "undefined";
- }
- v4l_info(client, "Preferred audio mode: %s\n", p);
-
- if ((audio_config & 0xf) == 0xf) {
- switch ((afc0 >> 3) & 0x3) {
- case 0: p = "system DK"; break;
- case 1: p = "system L"; break;
- case 2: p = "autodetect"; break;
- default: p = "undefined";
- }
- v4l_info(client, "Selected 65 MHz format: %s\n", p);
-
- switch (afc0 & 0x7) {
- case 0: p = "chroma"; break;
- case 1: p = "BTSC"; break;
- case 2: p = "EIAJ"; break;
- case 3: p = "A2-M"; break;
- case 4: p = "autodetect"; break;
- default: p = "undefined";
- }
- v4l_info(client, "Selected 45 MHz format: %s\n", p);
- }
-}
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
index ea669b1f084d..95093edc9186 100644
--- a/drivers/media/video/cx25840/cx25840-core.h
+++ b/drivers/media/video/cx25840/cx25840-core.h
@@ -47,6 +47,7 @@ struct cx25840_state {
u32 id;
u32 rev;
int is_cx25836;
+ int is_cx23885;
int is_initialized;
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
struct work_struct fw_work; /* work entry for fw load */
diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c
index e852024a5ea3..1ddf724a2c74 100644
--- a/drivers/media/video/cx25840/cx25840-firmware.c
+++ b/drivers/media/video/cx25840/cx25840-firmware.c
@@ -24,6 +24,7 @@
#include "cx25840-core.h"
#define FWFILE "v4l-cx25840.fw"
+#define FWFILE_CX23885 "v4l-cx23885-avcore-01.fw"
/*
* Mike Isely <isely@pobox.com> - The FWSEND parameter controls the
@@ -92,10 +93,14 @@ static int fw_write(struct i2c_client *client, u8 * data, int size)
int cx25840_loadfw(struct i2c_client *client)
{
+ struct cx25840_state *state = i2c_get_clientdata(client);
const struct firmware *fw = NULL;
u8 buffer[4], *ptr;
int size, send, retval;
+ if (state->is_cx23885)
+ firmware = FWFILE_CX23885;
+
if (request_firmware(&fw, firmware, FWDEV(client)) != 0) {
v4l_err(client, "unable to open firmware %s\n", firmware);
return -EINVAL;
diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c
index ced13febed89..6828f59b9d83 100644
--- a/drivers/media/video/cx25840/cx25840-vbi.c
+++ b/drivers/media/video/cx25840/cx25840-vbi.c
@@ -180,7 +180,7 @@ void cx25840_vbi_setup(struct i2c_client *client)
fsc/1000000,fsc%1000000);
v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, "
- "vblank %i , vactive %i, vblank656 %i, src_dec %i,"
+ "vblank %i, vactive %i, vblank656 %i, src_dec %i, "
"burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x,"
" sc 0x%06x\n",
hblank, hactive, vblank, vactive, vblank656,
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index ceb31d4a2512..49d3813a9b48 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -8,6 +8,7 @@ config VIDEO_CX88
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_IR
+ select VIDEO_WM8775 if VIDEO_HELPER_CHIPS_AUTO
---help---
This is a video4linux driver for Conexant 2388x based
TV cards.
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index 40ffd7a5579a..8735227f7e47 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -417,7 +417,7 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream,
buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
chip->buf = buf;
chip->dma_risc = dma;
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index f802b5653569..a99e9d5950aa 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -307,7 +307,7 @@ static int register_read(struct cx88_core *core, u32 address, u32 *value)
/* ------------------------------------------------------------------ */
-static int blackbird_mbox_func(void *priv, int command, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
+static int blackbird_mbox_func(void *priv, u32 command, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
{
struct cx8802_dev *dev = priv;
unsigned long timeout;
@@ -536,11 +536,12 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev)
dprintk(1,"Initialize codec\n");
retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
if (retval < 0) {
+
+ dev->mpeg_active = 0;
+
/* ping was not successful, reset and upload firmware */
cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */
- msleep(1);
cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */
- msleep(1);
retval = blackbird_load_firmware(dev);
if (retval < 0)
return retval;
@@ -562,7 +563,6 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev)
}
dprintk(0, "Firmware version is 0x%08x\n", version);
}
- msleep(1);
cx_write(MO_PINMUX_IO, 0x88); /* 656-8bit IO and enable MPEG parallel IO */
cx_clear(MO_INPUT_FORMAT, 0x100); /* chroma subcarrier lock to normal? */
@@ -570,40 +570,68 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev)
cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */
blackbird_codec_settings(dev);
- msleep(1);
- /* blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xef, 0xef);
- blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xf0, 0xf0);
- blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0x180, 0x180); */
blackbird_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
BLACKBIRD_FIELD1_SAA7115,
BLACKBIRD_FIELD2_SAA7115
);
- /* blackbird_api_cmd(dev, IVTV_API_ASSIGN_PLACEHOLDER, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); */
blackbird_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
BLACKBIRD_CUSTOM_EXTENSION_USR_DATA,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
- /* initialize the video input */
- blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
+ return 0;
+}
- msleep(1);
+static int blackbird_start_codec(struct file *file, void *priv)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+ struct cx88_core *core = dev->core;
+ /* start capturing to the host interface */
+ u32 reg;
- blackbird_api_cmd(dev, CX2341X_ENC_MUTE_VIDEO, 1, 0, BLACKBIRD_UNMUTE);
- msleep(1);
- blackbird_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, BLACKBIRD_UNMUTE);
- msleep(1);
+ int i;
+ int lastchange = -1;
+ int lastval = 0;
+
+ for (i = 0; (i < 10) && (i < (lastchange + 4)); i++) {
+ reg = cx_read(AUD_STATUS);
+
+ dprintk(1, "AUD_STATUS:%dL: 0x%x\n", i, reg);
+ if ((reg & 0x0F) != lastval) {
+ lastval = reg & 0x0F;
+ lastchange = i;
+ }
+ msleep(100);
+ }
+
+ /* unmute audio source */
+ cx_clear(AUD_VOL_CTL, (1 << 6));
+
+ blackbird_api_cmd(dev, CX2341X_ENC_REFRESH_INPUT, 0, 0);
+
+ /* initialize the video input */
+ blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
/* start capturing to the host interface */
- /* blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, 0, 0x13); */
blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
BLACKBIRD_MPEG_CAPTURE,
BLACKBIRD_RAW_BITS_NONE
);
- msleep(10);
- blackbird_api_cmd(dev, CX2341X_ENC_REFRESH_INPUT, 0,0);
+ dev->mpeg_active = 1;
+ return 0;
+}
+
+static int blackbird_stop_codec(struct cx8802_dev *dev)
+{
+ blackbird_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
+ BLACKBIRD_END_NOW,
+ BLACKBIRD_MPEG_CAPTURE,
+ BLACKBIRD_RAW_BITS_NONE
+ );
+
+ dev->mpeg_active = 0;
return 0;
}
@@ -833,6 +861,10 @@ static int vidioc_s_ext_ctrls (struct file *file, void *priv,
if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
return -EINVAL;
+
+ if (dev->mpeg_active)
+ blackbird_stop_codec(dev);
+
p = dev->params;
err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS);
if (!err) {
@@ -864,10 +896,9 @@ static int vidioc_s_frequency (struct file *file, void *priv,
struct cx8802_dev *dev = fh->dev;
struct cx88_core *core = dev->core;
- blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
- BLACKBIRD_END_NOW,
- BLACKBIRD_MPEG_CAPTURE,
- BLACKBIRD_RAW_BITS_NONE);
+ if (dev->mpeg_active)
+ blackbird_stop_codec(dev);
+
cx88_set_freq (core,f);
blackbird_initialize_codec(dev);
cx88_set_scale(dev->core, dev->width, dev->height,
@@ -1073,15 +1104,11 @@ static int mpeg_open(struct inode *inode, struct file *file)
static int mpeg_release(struct inode *inode, struct file *file)
{
struct cx8802_fh *fh = file->private_data;
- struct cx8802_dev *dev = NULL;
+ struct cx8802_dev *dev = fh->dev;
struct cx8802_driver *drv = NULL;
- /* blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, BLACKBIRD_END_NOW, 0, 0x13); */
- blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
- BLACKBIRD_END_NOW,
- BLACKBIRD_MPEG_CAPTURE,
- BLACKBIRD_RAW_BITS_NONE
- );
+ if (dev->mpeg_active)
+ blackbird_stop_codec(dev);
cx8802_cancel_buffers(fh->dev);
/* stop mpeg capture */
@@ -1107,6 +1134,10 @@ static ssize_t
mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct cx8802_fh *fh = file->private_data;
+ struct cx8802_dev *dev = fh->dev;
+
+ if (!dev->mpeg_active)
+ blackbird_start_codec(file, fh);
return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0,
file->f_flags & O_NONBLOCK);
@@ -1282,6 +1313,7 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv)
core->name);
host_setup(dev->core);
+ blackbird_initialize_codec(dev);
blackbird_register_video(dev);
/* initial device configuration: needed ? */
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index a4eb6a87a761..e6b7f518c56e 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include "cx88.h"
+#include "tea5767.h"
static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
@@ -245,6 +246,10 @@ static const struct cx88_board cx88_boards[] = {
}},
.radio = {
.type = CX88_RADIO,
+ .vmux = 3,
+ .gpio0 = 0x000040bf,
+ .gpio1 = 0x000080c0,
+ .gpio2 = 0x0000ff20,
},
},
[CX88_BOARD_WINFAST_DV2000] = {
@@ -297,22 +302,22 @@ static const struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0x0000bde2,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x0000bde6,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x0000bde6,
- .extadc = 1,
+ .audioroute = 1,
}},
.radio = {
.type = CX88_RADIO,
.gpio0 = 0x0000bd62,
- .extadc = 1,
+ .audioroute = 1,
},
.mpeg = CX88_MPEG_BLACKBIRD,
},
@@ -373,7 +378,7 @@ static const struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
- .extadc = 1,
+ .audioroute = 1,
}},
.radio = {
.type = CX88_RADIO,
@@ -544,7 +549,7 @@ static const struct cx88_board cx88_boards[] = {
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
- .extadc = 1,
+ .audioroute = 1,
}},
.mpeg = CX88_MPEG_BLACKBIRD,
},
@@ -667,22 +672,22 @@ static const struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0x00009d80,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x00009d76,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x00009d76,
- .extadc = 1,
+ .audioroute = 1,
}},
.radio = {
.type = CX88_RADIO,
.gpio0 = 0x00009d00,
- .extadc = 1,
+ .audioroute = 1,
},
.mpeg = CX88_MPEG_BLACKBIRD,
},
@@ -821,23 +826,23 @@ static const struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_COMPOSITE1,
.vmux = 0,
.gpio0 = 0x0000cd73,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 1,
.gpio0 = 0x0000cd73,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_TELEVISION,
.vmux = 3,
.gpio0 = 0x0000cdb3,
- .extadc = 1,
+ .audioroute = 1,
}},
.radio = {
.type = CX88_RADIO,
.vmux = 2,
.gpio0 = 0x0000cdf3,
- .extadc = 1,
+ .audioroute = 1,
},
.mpeg = CX88_MPEG_BLACKBIRD,
},
@@ -1105,12 +1110,12 @@ static const struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x3de6,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x3de6,
- .extadc = 1,
+ .audioroute = 1,
}},
.radio = {
.type = CX88_RADIO,
@@ -1335,17 +1340,17 @@ static const struct cx88_board cx88_boards[] = {
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0xe780,
- .extadc = 1,
+ .audioroute = 1,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0xe780,
- .extadc = 1,
+ .audioroute = 2,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0xe780,
- .extadc = 1,
+ .audioroute = 2,
}},
/* fixme: Add radio support */
.mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD,
@@ -1370,6 +1375,32 @@ static const struct cx88_board cx88_boards[] = {
.gpio0 = 0x07fa,
}},
},
+ [CX88_BOARD_PINNACLE_PCTV_HD_800i] = {
+ .name = "Pinnacle PCTV HD 800i",
+ .tuner_type = TUNER_XC5000,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x10ff,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x10ef,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x10ef,
+ .audioroute = 1,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
};
/* ------------------------------------------------------------------ */
@@ -1679,6 +1710,10 @@ static const struct cx88_subid cx88_subids[] = {
.subvendor = 0x1421,
.subdevice = 0x0390,
.card = CX88_BOARD_ADSTECH_PTV_390,
+ },{
+ .subvendor = 0x11bd,
+ .subdevice = 0x0051,
+ .card = CX88_BOARD_PINNACLE_PCTV_HD_800i,
},
};
@@ -1846,6 +1881,36 @@ static void dvico_fusionhdtv_hybrid_init(struct cx88_core *core)
}
/* ----------------------------------------------------------------------- */
+/* Tuner callback function. Currently only needed for the Pinnacle *
+ * PCTV HD 800i with an xc5000 sillicon tuner. This is used for both *
+ * analog tuner attach (tuner-core.c) and dvb tuner attach (cx88-dvb.c) */
+
+int cx88_tuner_callback(void *priv, int command, int arg)
+{
+ struct i2c_algo_bit_data *i2c_algo = priv;
+ struct cx88_core *core = i2c_algo->data;
+
+ switch(core->boardnr) {
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ if(command == 0) { /* This is the reset command from xc5000 */
+ /* Reset XC5000 tuner via SYS_RSTO_pin */
+ cx_write(MO_SRST_IO, 0);
+ msleep(10);
+ cx_write(MO_SRST_IO, 1);
+ return 0;
+ }
+ else {
+ printk(KERN_ERR
+ "xc5000: unknown tuner callback command.\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ return 0; /* Should never be here */
+}
+EXPORT_SYMBOL(cx88_tuner_callback);
+
+/* ----------------------------------------------------------------------- */
static void cx88_card_list(struct cx88_core *core, struct pci_dev *pci)
{
@@ -1979,6 +2044,23 @@ static void cx88_card_setup(struct cx88_core *core)
core->name, i);
}
break;
+ case CX88_BOARD_MSI_TVANYWHERE_MASTER:
+ {
+ struct v4l2_priv_tun_config tea5767_cfg;
+ struct tea5767_ctrl ctl;
+
+ memset(&ctl, 0, sizeof(ctl));
+
+ ctl.high_cut = 1;
+ ctl.st_noise = 1;
+ ctl.deemph_75 = 1;
+ ctl.xtal_freq = TEA5767_HIGH_LO_13MHz;
+
+ tea5767_cfg.tuner = TUNER_TEA5767;
+ tea5767_cfg.priv = &ctl;
+
+ cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tea5767_cfg);
+ }
}
}
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index 62e8dd24c5f5..01e2ac98970b 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -220,7 +220,7 @@ cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
videobuf_dma_unmap(q, dma);
videobuf_dma_free(dma);
btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc);
- buf->vb.state = STATE_NEEDS_INIT;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
/* ------------------------------------------------------------------ */
@@ -538,7 +538,7 @@ void cx88_wakeup(struct cx88_core *core,
do_gettimeofday(&buf->vb.ts);
dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
count, buf->count);
- buf->vb.state = STATE_DONE;
+ buf->vb.state = VIDEOBUF_DONE;
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
}
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index fce19caf9d04..f7b41eb1bb5a 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -40,6 +40,8 @@
#include "cx22702.h"
#include "or51132.h"
#include "lgdt330x.h"
+#include "s5h1409.h"
+#include "xc5000.h"
#include "nxt200x.h"
#include "cx24123.h"
#include "isl6421.h"
@@ -371,6 +373,22 @@ static struct cx24123_config kworld_dvbs_100_config = {
.lnb_polarity = 1,
};
+static struct s5h1409_config pinnacle_pctv_hd_800i_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_PARALLEL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
+ .i2c_address = 0x64,
+ .if_khz = 5380,
+ .tuner_callback = cx88_tuner_callback,
+};
+
static int dvb_register(struct cx8802_dev *dev)
{
/* init struct videobuf_dvb */
@@ -625,6 +643,21 @@ static int dvb_register(struct cx8802_dev *dev)
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);
+ 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);
+ }
+ break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
dev->core->name);
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
index c8b1c50625f4..566b26af523e 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -109,26 +109,32 @@ static int attach_inform(struct i2c_client *client)
if (core->board.radio_type != UNSET) {
if ((core->board.radio_addr==ADDR_UNSET)||(core->board.radio_addr==client->addr)) {
- tun_setup.mode_mask = T_RADIO;
- tun_setup.type = core->board.radio_type;
- tun_setup.addr = core->board.radio_addr;
-
+ tun_setup.mode_mask = T_RADIO;
+ tun_setup.type = core->board.radio_type;
+ tun_setup.addr = core->board.radio_addr;
+ tun_setup.tuner_callback = cx88_tuner_callback;
client->driver->command (client, TUNER_SET_TYPE_ADDR, &tun_setup);
}
}
if (core->board.tuner_type != UNSET) {
if ((core->board.tuner_addr==ADDR_UNSET)||(core->board.tuner_addr==client->addr)) {
- tun_setup.mode_mask = T_ANALOG_TV;
- tun_setup.type = core->board.tuner_type;
- tun_setup.addr = core->board.tuner_addr;
-
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.type = core->board.tuner_type;
+ tun_setup.addr = core->board.tuner_addr;
+ tun_setup.tuner_callback = cx88_tuner_callback;
client->driver->command (client,TUNER_SET_TYPE_ADDR, &tun_setup);
}
}
- if (core->board.tda9887_conf)
- client->driver->command(client, TDA9887_SET_CONFIG, &core->board.tda9887_conf);
+ if (core->board.tda9887_conf) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &core->board.tda9887_conf;
+
+ client->driver->command(client, TUNER_SET_CONFIG, &tda9887_cfg);
+ }
return 0;
}
@@ -176,6 +182,7 @@ static char *i2c_devs[128] = {
[ 0xa0 >> 1 ] = "eeprom",
[ 0xc0 >> 1 ] = "tuner (analog)",
[ 0xc2 >> 1 ] = "tuner (analog/dvb)",
+ [ 0xc8 >> 1 ] = "xc5000",
};
static void do_i2c_scan(char *name, struct i2c_client *c)
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index e52de3968c63..bb0911b4d2f6 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -305,6 +305,11 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
ir->mask_keycode = 0xfa;
ir->polling = 50; /* ms */
break;
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ ir_codes = ir_codes_pinnacle_pctv_hd;
+ ir_type = IR_TYPE_RC5;
+ ir->sampling = 1;
+ break;
}
if (NULL == ir_codes) {
@@ -443,6 +448,7 @@ void cx88_ir_irq(struct cx88_core *core)
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7);
ir_dprintk("biphase decoded: %x\n", ircode);
if ((ircode & 0xfffff000) != 0x3000)
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index 448c67380945..0aedbeaf94cd 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -102,7 +102,7 @@ static int cx8802_start_dma(struct cx8802_dev *dev,
cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);
udelay(100);
cx_write(MO_PINMUX_IO, 0x00);
- cx_write(TS_HW_SOP_CNTRL,0x47<<16|188<<4|0x01);
+ cx_write(TS_HW_SOP_CNTRL, 0x47<<16|188<<4|0x01);
switch (core->boardnr) {
case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
@@ -117,6 +117,15 @@ static int cx8802_start_dma(struct cx8802_dev *dev,
break;
case CX88_BOARD_HAUPPAUGE_HVR1300:
break;
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ /* Enable MPEG parallel IO and video signal pins */
+ cx_write(MO_PINMUX_IO, 0x88);
+ cx_write(TS_HW_SOP_CNTRL, (0x47 << 16) | (188 << 4));
+ dev->ts_gen_cntrl = 5;
+ cx_write(TS_SOP_STAT, 0);
+ cx_write(TS_VALERR_CNTRL, 0);
+ udelay(100);
+ break;
default:
cx_write(TS_SOP_STAT, 0x00);
break;
@@ -195,7 +204,7 @@ static int cx8802_restart_queue(struct cx8802_dev *dev,
list_del(&buf->vb.queue);
list_add_tail(&buf->vb.queue,&q->active);
cx8802_start_dma(dev, q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(1,"[%p/%d] restart_queue - first active\n",
@@ -206,7 +215,7 @@ static int cx8802_restart_queue(struct cx8802_dev *dev,
prev->fmt == buf->fmt) {
list_del(&buf->vb.queue);
list_add_tail(&buf->vb.queue,&q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk(1,"[%p/%d] restart_queue - move to active\n",
@@ -242,7 +251,7 @@ int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev,
if (0 != buf->vb.baddr && buf->vb.bsize < size)
return -EINVAL;
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
buf->vb.width = dev->ts_packet_size;
buf->vb.height = dev->ts_packet_count;
buf->vb.size = size;
@@ -254,7 +263,7 @@ int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev,
dma->sglist,
buf->vb.width, buf->vb.height, 0);
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
@@ -276,7 +285,7 @@ void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
dprintk( 1, "queue is empty - first active\n" );
list_add_tail(&buf->vb.queue,&cx88q->active);
cx8802_start_dma(dev, cx88q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = cx88q->count++;
mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(1,"[%p/%d] %s - first active\n",
@@ -286,7 +295,7 @@ void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
dprintk( 1, "queue is not empty - append to active\n" );
prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue);
list_add_tail(&buf->vb.queue,&cx88q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = cx88q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk( 1, "[%p/%d] %s - append to active\n",
@@ -306,7 +315,7 @@ static void do_cancel_buffers(struct cx8802_dev *dev, char *reason, int restart)
while (!list_empty(&q->active)) {
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
list_del(&buf->vb.queue);
- buf->vb.state = STATE_ERROR;
+ buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
dprintk(1,"[%p/%d] %s - dma=0x%08lx\n",
buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
@@ -437,10 +446,7 @@ static irqreturn_t cx8802_irq(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-/* ----------------------------------------------------------- */
-/* exported stuff */
-
-int cx8802_init_common(struct cx8802_dev *dev)
+static int cx8802_init_common(struct cx8802_dev *dev)
{
struct cx88_core *core = dev->core;
int err;
@@ -488,7 +494,7 @@ int cx8802_init_common(struct cx8802_dev *dev)
return 0;
}
-void cx8802_fini_common(struct cx8802_dev *dev)
+static void cx8802_fini_common(struct cx8802_dev *dev)
{
dprintk( 2, "cx8802_fini_common\n" );
cx8802_stop_dma(dev);
@@ -504,7 +510,7 @@ void cx8802_fini_common(struct cx8802_dev *dev)
/* ----------------------------------------------------------- */
-int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)
+static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)
{
struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
struct cx88_core *core = dev->core;
@@ -530,7 +536,7 @@ int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)
return 0;
}
-int cx8802_resume_common(struct pci_dev *pci_dev)
+static int cx8802_resume_common(struct pci_dev *pci_dev)
{
struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
struct cx88_core *core = dev->core;
@@ -874,9 +880,6 @@ EXPORT_SYMBOL(cx8802_buf_prepare);
EXPORT_SYMBOL(cx8802_buf_queue);
EXPORT_SYMBOL(cx8802_cancel_buffers);
-EXPORT_SYMBOL(cx8802_init_common);
-EXPORT_SYMBOL(cx8802_fini_common);
-
EXPORT_SYMBOL(cx8802_register_driver);
EXPORT_SYMBOL(cx8802_unregister_driver);
EXPORT_SYMBOL(cx8802_get_driver);
diff --git a/drivers/media/video/cx88/cx88-vbi.c b/drivers/media/video/cx88/cx88-vbi.c
index babb08556406..d96ecfcf393a 100644
--- a/drivers/media/video/cx88/cx88-vbi.c
+++ b/drivers/media/video/cx88/cx88-vbi.c
@@ -130,7 +130,7 @@ void cx8800_vbi_timeout(unsigned long data)
while (!list_empty(&q->active)) {
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
list_del(&buf->vb.queue);
- buf->vb.state = STATE_ERROR;
+ buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->core->name,
buf, buf->vb.i, (unsigned long)buf->risc.dma);
@@ -168,7 +168,7 @@ vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
if (0 != buf->vb.baddr && buf->vb.bsize < size)
return -EINVAL;
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
buf->vb.width = VBI_LINE_LENGTH;
buf->vb.height = VBI_LINE_COUNT;
@@ -183,7 +183,7 @@ vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
buf->vb.width, 0,
buf->vb.height);
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
@@ -207,7 +207,7 @@ vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue,&q->active);
cx8800_start_vbi_dma(dev, q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2,"[%p/%d] vbi_queue - first active\n",
@@ -216,7 +216,7 @@ vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
} else {
prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue);
list_add_tail(&buf->vb.queue,&q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk(2,"[%p/%d] buffer_queue - append to active\n",
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index c84dafbdb991..7f1931aed207 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -392,13 +392,41 @@ int cx88_video_mux(struct cx88_core *core, unsigned int input)
break;
}
- if (core->board.mpeg & CX88_MPEG_BLACKBIRD) {
- /* sets sound input from external adc */
- if (INPUT(input).extadc)
+ /* if there are audioroutes defined, we have an external
+ ADC to deal with audio */
+
+ if (INPUT(input).audioroute) {
+
+ /* cx2388's C-ADC is connected to the tuner only.
+ When used with S-Video, that ADC is busy dealing with
+ chroma, so an external must be used for baseband audio */
+
+ if (INPUT(input).type != CX88_VMUX_TELEVISION &&
+ INPUT(input).type != CX88_RADIO) {
+ /* "ADC mode" */
+ cx_write(AUD_I2SCNTL, 0x1);
cx_set(AUD_CTL, EN_I2SIN_ENABLE);
- else
+ } else {
+ /* Normal mode */
+ cx_write(AUD_I2SCNTL, 0x0);
cx_clear(AUD_CTL, EN_I2SIN_ENABLE);
+ }
+
+ /* The wm8775 module has the "2" route hardwired into
+ the initialization. Some boards may use different
+ routes for different inputs. HVR-1300 surely does */
+ if (core->board.audio_chip &&
+ core->board.audio_chip == AUDIO_CHIP_WM8775) {
+ struct v4l2_routing route;
+
+ route.input = INPUT(input).audioroute;
+ cx88_call_i2c_clients(core,
+ VIDIOC_INT_S_AUDIO_ROUTING, &route);
+
+ }
+
}
+
return 0;
}
EXPORT_SYMBOL(cx88_video_mux);
@@ -486,7 +514,7 @@ static int restart_video_queue(struct cx8800_dev *dev,
if (NULL == prev) {
list_move_tail(&buf->vb.queue, &q->active);
start_video_dma(dev, q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2,"[%p/%d] restart_queue - first active\n",
@@ -496,7 +524,7 @@ static int restart_video_queue(struct cx8800_dev *dev,
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
list_move_tail(&buf->vb.queue, &q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk(2,"[%p/%d] restart_queue - move to active\n",
@@ -553,7 +581,7 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
init_buffer = 1;
}
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
init_buffer = 1;
if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
goto fail;
@@ -601,7 +629,7 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
(unsigned long)buf->risc.dma);
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
@@ -625,14 +653,14 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
if (!list_empty(&q->queued)) {
list_add_tail(&buf->vb.queue,&q->queued);
- buf->vb.state = STATE_QUEUED;
+ buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2,"[%p/%d] buffer_queue - append to queued\n",
buf, buf->vb.i);
} else if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue,&q->active);
start_video_dma(dev, q, buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2,"[%p/%d] buffer_queue - first active\n",
@@ -644,7 +672,7 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
list_add_tail(&buf->vb.queue,&q->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk(2,"[%p/%d] buffer_queue - append to active\n",
@@ -652,7 +680,7 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
} else {
list_add_tail(&buf->vb.queue,&q->queued);
- buf->vb.state = STATE_QUEUED;
+ buf->vb.state = VIDEOBUF_QUEUED;
dprintk(2,"[%p/%d] buffer_queue - first queued\n",
buf, buf->vb.i);
}
@@ -822,8 +850,8 @@ video_poll(struct file *file, struct poll_table_struct *wait)
return POLLERR;
}
poll_wait(file, &buf->vb.done, wait);
- if (buf->vb.state == STATE_DONE ||
- buf->vb.state == STATE_ERROR)
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
return POLLIN|POLLRDNORM;
return 0;
}
@@ -1496,7 +1524,7 @@ static void cx8800_vid_timeout(unsigned long data)
while (!list_empty(&q->active)) {
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
list_del(&buf->vb.queue);
- buf->vb.state = STATE_ERROR;
+ buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", core->name,
buf, buf->vb.i, (unsigned long)buf->risc.dma);
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index eb296bdecb1e..4e823f2a539a 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -210,6 +210,7 @@ extern struct sram_channel cx88_sram_channels[];
#define CX88_BOARD_TE_DTV_250_OEM_SWANN 55
#define CX88_BOARD_HAUPPAUGE_HVR1300 56
#define CX88_BOARD_ADSTECH_PTV_390 57
+#define CX88_BOARD_PINNACLE_PCTV_HD_800i 58
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
@@ -228,7 +229,7 @@ struct cx88_input {
enum cx88_itype type;
u32 gpio0, gpio1, gpio2, gpio3;
unsigned int vmux:2;
- unsigned int extadc:1;
+ unsigned int audioroute:2;
};
struct cx88_board {
@@ -461,6 +462,7 @@ struct cx8802_dev {
u32 mailbox;
int width;
int height;
+ unsigned char mpeg_active; /* nonzero if mpeg encoder is active */
/* mpeg params */
struct cx2341x_mpeg_params params;
@@ -588,6 +590,7 @@ extern void cx88_call_i2c_clients(struct cx88_core *core,
/* ----------------------------------------------------------- */
/* cx88-cards.c */
+extern int cx88_tuner_callback(void *dev, int command, int arg);
extern int cx88_get_resources(const struct cx88_core *core,
struct pci_dev *pci);
extern struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr);
@@ -633,12 +636,6 @@ int cx8802_buf_prepare(struct videobuf_queue *q,struct cx8802_dev *dev,
void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf);
void cx8802_cancel_buffers(struct cx8802_dev *dev);
-int cx8802_init_common(struct cx8802_dev *dev);
-void cx8802_fini_common(struct cx8802_dev *dev);
-
-int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state);
-int cx8802_resume_common(struct pci_dev *pci_dev);
-
/* ----------------------------------------------------------- */
/* cx88-video.c*/
extern const u32 cx88_user_ctrls[];
diff --git a/drivers/media/video/dpc7146.c b/drivers/media/video/dpc7146.c
index 255dae303708..566e479e2629 100644
--- a/drivers/media/video/dpc7146.c
+++ b/drivers/media/video/dpc7146.c
@@ -87,11 +87,24 @@ struct dpc
int cur_input; /* current input */
};
+static int dpc_check_clients(struct device *dev, void *data)
+{
+ struct dpc* dpc = data;
+ struct i2c_client *client = i2c_verify_client(dev);
+
+ if( !client )
+ return 0;
+
+ if( I2C_SAA7111A == client->addr )
+ dpc->saa7111a = client;
+
+ return 0;
+}
+
/* fixme: add vbi stuff here */
static int dpc_probe(struct saa7146_dev* dev)
{
struct dpc* dpc = NULL;
- struct i2c_client *client;
dpc = kzalloc(sizeof(struct dpc), GFP_KERNEL);
if( NULL == dpc ) {
@@ -115,9 +128,7 @@ static int dpc_probe(struct saa7146_dev* dev)
}
/* loop through all i2c-devices on the bus and look who is there */
- list_for_each_entry(client, &dpc->i2c_adapter.clients, list)
- if( I2C_SAA7111A == client->addr )
- dpc->saa7111a = client;
+ device_for_each_child(&dpc->i2c_adapter.dev, dpc, dpc_check_clients);
/* check if all devices are present */
if( 0 == dpc->saa7111a ) {
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
index c1127802ad9c..abbd38c1ebba 100644
--- a/drivers/media/video/em28xx/Kconfig
+++ b/drivers/media/video/em28xx/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_EM28XX
tristate "Empia EM2800/2820/2840 USB video capture support"
- depends on VIDEO_V4L1 && I2C && INPUT
+ depends on VIDEO_DEV && I2C && INPUT
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_IR
@@ -11,3 +11,18 @@ config VIDEO_EM28XX
To compile this driver as a module, choose M here: the
module will be called em28xx
+
+config VIDEO_EM28XX_ALSA
+ depends on VIDEO_EM28XX
+ tristate "Empia EM28xx ALSA audio module"
+ ---help---
+ This is an ALSA driver for some Empia 28xx based TV cards.
+
+ This is not required for em2800/em2820/em2821 boards. However,
+ newer em28xx devices uses Vendor Class for audio, instead of
+ implementing the USB Audio Class. For those chips, this module
+ will enable digital audio.
+
+ To compile this driver as a module, choose M here: the
+ module will be called em28xx-alsa
+
diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
index 826d0e340753..0924550992d0 100644
--- a/drivers/media/video/em28xx/Makefile
+++ b/drivers/media/video/em28xx/Makefile
@@ -1,6 +1,12 @@
em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \
em28xx-input.o
+em28xx-alsa-objs := em28xx-audio.o
+
obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o
+obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o
EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
new file mode 100644
index 000000000000..941357c4f3f5
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -0,0 +1,489 @@
+/*
+ * Empiatech em28x1 audio extension
+ *
+ * Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
+ *
+ * Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * - Port to work with the in-kernel driver
+ * - Several cleanups
+ *
+ * This driver is based on my previous au600 usb pstn audio driver
+ * and inherits all the copyrights
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/sound.h>
+#include <linux/spinlock.h>
+#include <linux/soundcard.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <media/v4l2-common.h>
+#include "em28xx.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_INFO "em28xx-audio %s: " fmt, \
+ __FUNCTION__, ##arg); \
+ } while (0)
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+
+static int em28xx_isoc_audio_deinit(struct em28xx *dev)
+{
+ int i;
+
+ dprintk("Stopping isoc\n");
+ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
+ usb_kill_urb(dev->adev->urb[i]);
+ usb_free_urb(dev->adev->urb[i]);
+ dev->adev->urb[i] = NULL;
+ }
+
+ return 0;
+}
+
+static void em28xx_audio_isocirq(struct urb *urb)
+{
+ struct em28xx *dev = urb->context;
+ int i;
+ unsigned int oldptr;
+ unsigned long flags;
+ int period_elapsed = 0;
+ int status;
+ unsigned char *cp;
+ unsigned int stride;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ if (dev->adev->capture_pcm_substream) {
+ substream = dev->adev->capture_pcm_substream;
+ runtime = substream->runtime;
+ stride = runtime->frame_bits >> 3;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int length =
+ urb->iso_frame_desc[i].actual_length / stride;
+ cp = (unsigned char *)urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset;
+
+ if (!length)
+ continue;
+
+ spin_lock_irqsave(&dev->adev->slock, flags);
+
+ oldptr = dev->adev->hwptr_done_capture;
+ dev->adev->hwptr_done_capture += length;
+ if (dev->adev->hwptr_done_capture >=
+ runtime->buffer_size)
+ dev->adev->hwptr_done_capture -=
+ runtime->buffer_size;
+
+ dev->adev->capture_transfer_done += length;
+ if (dev->adev->capture_transfer_done >=
+ runtime->period_size) {
+ dev->adev->capture_transfer_done -=
+ runtime->period_size;
+ period_elapsed = 1;
+ }
+
+ spin_unlock_irqrestore(&dev->adev->slock, flags);
+
+ if (oldptr + length >= runtime->buffer_size) {
+ unsigned int cnt =
+ runtime->buffer_size - oldptr - 1;
+ memcpy(runtime->dma_area + oldptr * stride, cp,
+ cnt * stride);
+ memcpy(runtime->dma_area, cp + cnt,
+ length * stride - cnt * stride);
+ } else {
+ memcpy(runtime->dma_area + oldptr * stride, cp,
+ length * stride);
+ }
+ }
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+ }
+ urb->status = 0;
+
+ if (dev->adev->shutdown)
+ return;
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status < 0) {
+ em28xx_errdev("resubmit of audio urb failed (error=%i)\n",
+ status);
+ }
+ return;
+}
+
+static int em28xx_init_audio_isoc(struct em28xx *dev)
+{
+ int i, errCode;
+ const int sb_size = EM28XX_NUM_AUDIO_PACKETS *
+ EM28XX_AUDIO_MAX_PACKET_SIZE;
+
+ dprintk("Starting isoc transfers\n");
+
+ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
+ struct urb *urb;
+ int j, k;
+
+ dev->adev->transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
+ if (!dev->adev->transfer_buffer[i])
+ return -ENOMEM;
+
+ memset(dev->adev->transfer_buffer[i], 0x80, sb_size);
+ urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ urb->dev = dev->udev;
+ urb->context = dev;
+ urb->pipe = usb_rcvisocpipe(dev->udev, 0x83);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = dev->adev->transfer_buffer[i];
+ urb->interval = 1;
+ urb->complete = em28xx_audio_isocirq;
+ urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS;
+ urb->transfer_buffer_length = sb_size;
+
+ for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS;
+ j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length =
+ EM28XX_AUDIO_MAX_PACKET_SIZE;
+ }
+ dev->adev->urb[i] = urb;
+ }
+
+ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
+ errCode = usb_submit_urb(dev->adev->urb[i], GFP_ATOMIC);
+ if (errCode) {
+ em28xx_isoc_audio_deinit(dev);
+
+ return errCode;
+ }
+ }
+
+ return 0;
+}
+
+static int em28xx_cmd(struct em28xx *dev, int cmd, int arg)
+{
+ dprintk("%s transfer\n", (dev->adev->capture_stream == STREAM_ON)?
+ "stop" : "start");
+
+ switch (cmd) {
+ case EM28XX_CAPTURE_STREAM_EN:
+ if (dev->adev->capture_stream == STREAM_OFF && arg == 1) {
+ dev->adev->capture_stream = STREAM_ON;
+ em28xx_init_audio_isoc(dev);
+ } else if (dev->adev->capture_stream == STREAM_ON && arg == 0) {
+ dev->adev->capture_stream = STREAM_OFF;
+ em28xx_isoc_audio_deinit(dev);
+ } else {
+ printk(KERN_ERR "An underrun very likely occurred. "
+ "Ignoring it.\n");
+ }
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
+ size_t size)
+{
+ struct snd_pcm_runtime *runtime = subs->runtime;
+
+ dprintk("Alocating vbuffer\n");
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes > size)
+ return 0;
+
+ vfree(runtime->dma_area);
+ }
+ runtime->dma_area = vmalloc(size);
+ if (!runtime->dma_area)
+ return -ENOMEM;
+
+ runtime->dma_bytes = size;
+
+ return 0;
+}
+
+static struct snd_pcm_hardware snd_em28xx_hw_capture = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
+
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
+ .period_bytes_min = 64, /* 12544/2, */
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98, /* 12544, */
+};
+
+static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret = 0;
+
+ dprintk("opening device and trying to acquire exclusive lock\n");
+
+ /* Sets volume, mute, etc */
+ dev->mute = 0;
+ ret = em28xx_audio_analog_set(dev);
+ if (ret < 0)
+ goto err;
+
+ runtime->hw = snd_em28xx_hw_capture;
+ if (dev->alt == 0 && dev->adev->users == 0) {
+ int errCode;
+ dev->alt = 7;
+ errCode = usb_set_interface(dev->udev, 0, 7);
+ dprintk("changing alternate number to 7\n");
+ }
+
+ dev->adev->users++;
+
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ dev->adev->capture_pcm_substream = substream;
+ runtime->private_data = dev;
+
+ return 0;
+err:
+ printk(KERN_ERR "Error while configuring em28xx mixer\n");
+ return ret;
+}
+
+static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+ dev->adev->users--;
+
+ dprintk("closing device\n");
+
+ dev->mute = 1;
+ em28xx_audio_analog_set(dev);
+
+ if (dev->adev->users == 0 && dev->adev->shutdown == 1) {
+ dprintk("audio users: %d\n", dev->adev->users);
+ dprintk("disabling audio stream!\n");
+ dev->adev->shutdown = 0;
+ dprintk("released lock\n");
+ em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0);
+ }
+ return 0;
+}
+
+static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ unsigned int channels, rate, format;
+ int ret;
+
+ dprintk("Setting capture parameters\n");
+
+ ret = snd_pcm_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ format = params_format(hw_params);
+ rate = params_rate(hw_params);
+ channels = params_channels(hw_params);
+
+ /* TODO: set up em28xx audio chip to deliver the correct audio format,
+ current default is 48000hz multiplexed => 96000hz mono
+ which shouldn't matter since analogue TV only supports mono */
+ return 0;
+}
+
+static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+
+ dprintk("Stop capture, if needed\n");
+
+ if (dev->adev->capture_stream == STREAM_ON)
+ em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0);
+
+ return 0;
+}
+
+static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+
+ dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START)?
+ "start": "stop");
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 1);
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ dev->adev->shutdown = 1;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
+ *substream)
+{
+ struct em28xx *dev;
+
+ snd_pcm_uframes_t hwptr_done;
+ dev = snd_pcm_substream_chip(substream);
+ hwptr_done = dev->adev->hwptr_done_capture;
+
+ return hwptr_done;
+}
+
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops snd_em28xx_pcm_capture = {
+ .open = snd_em28xx_capture_open,
+ .close = snd_em28xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_em28xx_hw_capture_params,
+ .hw_free = snd_em28xx_hw_capture_free,
+ .prepare = snd_em28xx_prepare,
+ .trigger = snd_em28xx_capture_trigger,
+ .pointer = snd_em28xx_capture_pointer,
+ .page = snd_pcm_get_vmalloc_page,
+};
+
+static int em28xx_audio_init(struct em28xx *dev)
+{
+ struct em28xx_audio *adev;
+ struct snd_pcm *pcm;
+ struct snd_card *card;
+ static int devnr;
+ int ret, err;
+
+ printk(KERN_INFO "em28xx-audio.c: probing for em28x1 "
+ "non standard usbaudio\n");
+ printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
+ "Rechberger\n");
+
+ adev = kzalloc(sizeof(*adev), GFP_KERNEL);
+ if (!adev) {
+ printk(KERN_ERR "em28xx-audio.c: out of memory\n");
+ return -1;
+ }
+ card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE, 0);
+ if (card == NULL) {
+ kfree(adev);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&adev->slock);
+ ret = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture);
+ pcm->info_flags = 0;
+ pcm->private_data = dev;
+ strcpy(pcm->name, "Empia 28xx Capture");
+ strcpy(card->driver, "Empia Em28xx Audio");
+ strcpy(card->shortname, "Em28xx Audio");
+ strcpy(card->longname, "Empia Em28xx Audio");
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return -ENOMEM;
+ }
+ adev->sndcard = card;
+ adev->udev = dev->udev;
+ dev->adev = adev;
+
+ return 0;
+}
+
+static int em28xx_audio_fini(struct em28xx *dev)
+{
+ if (dev == NULL)
+ return 0;
+
+ if (dev->adev) {
+ snd_card_free(dev->adev->sndcard);
+ kfree(dev->adev);
+ dev->adev = NULL;
+ }
+
+ return 0;
+}
+
+static struct em28xx_ops audio_ops = {
+ .id = EM28XX_AUDIO,
+ .name = "Em28xx Audio Extension",
+ .init = em28xx_audio_init,
+ .fini = em28xx_audio_fini,
+};
+
+static int __init em28xx_alsa_register(void)
+{
+ return em28xx_register_extension(&audio_ops);
+}
+
+static void __exit em28xx_alsa_unregister(void)
+{
+ em28xx_unregister_extension(&audio_ops);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_DESCRIPTION("Em28xx Audio driver");
+
+module_init(em28xx_alsa_register);
+module_exit(em28xx_alsa_unregister);
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 418ea8b7f85a..2159d0160df2 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -1,5 +1,6 @@
/*
- em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
+ em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB
+ video capture devices
Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
Markus Rechberger <mrechberger@gmail.com>
@@ -35,294 +36,735 @@
#include <media/v4l2-common.h>
#include "em28xx.h"
+#include "tuner-xc2028.h"
+
+static int tuner = -1;
+module_param(tuner, int, 0444);
+MODULE_PARM_DESC(tuner, "tuner type");
+
+static unsigned int disable_ir;
+module_param(disable_ir, int, 0444);
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+
+struct em28xx_hash_table {
+ unsigned long hash;
+ unsigned int model;
+ unsigned int tuner;
+};
+
+/* Boards supported by driver */
+
+#define EM2800_BOARD_UNKNOWN 0
+#define EM2820_BOARD_UNKNOWN 1
+#define EM2820_BOARD_TERRATEC_CINERGY_250 2
+#define EM2820_BOARD_PINNACLE_USB_2 3
+#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4
+#define EM2820_BOARD_MSI_VOX_USB_2 5
+#define EM2800_BOARD_TERRATEC_CINERGY_200 6
+#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7
+#define EM2800_BOARD_KWORLD_USB2800 8
+#define EM2820_BOARD_PINNACLE_DVC_90 9
+#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10
+#define EM2880_BOARD_TERRATEC_HYBRID_XS 11
+#define EM2820_BOARD_KWORLD_PVRTV2800RF 12
+#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13
+#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14
+#define EM2800_BOARD_VGEAR_POCKETTV 15
+#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 16
struct em28xx_board em28xx_boards[] = {
[EM2800_BOARD_UNKNOWN] = {
.name = "Unknown EM2800 video grabber",
.is_em2800 = 1,
.vchannels = 2,
- .norm = VIDEO_MODE_PAL,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2820_BOARD_UNKNOWN] = {
- .name = "Unknown EM2820/2840 video grabber",
+ .name = "Unknown EM2750/28xx video grabber",
.is_em2800 = 0,
- .vchannels = 2,
- .norm = VIDEO_MODE_PAL,
- .tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
- .decoder = EM28XX_SAA7113,
- .input = {{
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = 1,
- },{
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = 1,
- }},
+ .tuner_type = TUNER_ABSENT,
},
[EM2820_BOARD_KWORLD_PVRTV2800RF] = {
.name = "Kworld PVR TV 2800 RF",
.is_em2800 = 0,
.vchannels = 2,
- .norm = VIDEO_MODE_PAL,
+ .tuner_type = TUNER_TEMIC_PAL,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2820_BOARD_TERRATEC_CINERGY_250] = {
.name = "Terratec Cinergy 250 USB",
.vchannels = 3,
- .norm = VIDEO_MODE_PAL,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2820_BOARD_PINNACLE_USB_2] = {
.name = "Pinnacle PCTV USB 2",
.vchannels = 3,
- .norm = VIDEO_MODE_PAL,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = 0,
- },{
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = {
.name = "Hauppauge WinTV USB 2",
.vchannels = 3,
- .norm = VIDEO_MODE_NTSC,
.tuner_type = TUNER_PHILIPS_FM1236_MK3,
- .tda9887_conf = TDA9887_PRESENT|TDA9887_PORT1_ACTIVE|TDA9887_PORT2_ACTIVE,
- .has_tuner = 1,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT1_ACTIVE|
+ TDA9887_PORT2_ACTIVE,
.decoder = EM28XX_TVP5150,
.has_msp34xx = 1,
/*FIXME: S-Video not tested */
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
.amux = MSP_INPUT_DEFAULT,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
.amux = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART),
- }},
+ } },
},
- [EM2820_BOARD_MSI_VOX_USB_2] = {
- .name = "MSI VOX USB 2.0",
- .vchannels = 3,
- .norm = VIDEO_MODE_PAL,
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .tda9887_conf = TDA9887_PRESENT|TDA9887_PORT1_ACTIVE|TDA9887_PORT2_ACTIVE,
- .has_tuner = 1,
- .decoder = EM28XX_SAA7114,
- .input = {{
+ [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
+ .name = "Hauppauge WinTV HVR 900",
+ .vchannels = 3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .mts_firmware = 1,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = 0,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = 1,
+ } },
+ },
+ [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950] = {
+ .name = "Hauppauge WinTV HVR 950",
+ .vchannels = 3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .mts_firmware = 1,
+ .has_12mhz_i2s = 1,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE4,
+ .vmux = TVP5150_COMPOSITE0,
.amux = 0,
- },{
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
+ .vmux = TVP5150_COMPOSITE1,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
+ .vmux = TVP5150_SVIDEO,
.amux = 1,
- }},
+ } },
+
+ /* gpio's 4, 1, 0 */
+ .analog_gpio = 0x003d2d,
+ },
+ [EM2880_BOARD_TERRATEC_HYBRID_XS] = {
+ .name = "Terratec Hybrid XS",
+ .vchannels = 3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = 0,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = 1,
+ } },
+ },
+ /* maybe there's a reason behind it why Terratec sells the Hybrid XS
+ as Prodigy XS with a different PID, let's keep it separated for now
+ maybe we'll need it lateron */
+ [EM2880_BOARD_TERRATEC_PRODIGY_XS] = {
+ .name = "Terratec Prodigy XS",
+ .vchannels = 3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = 0,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = 1,
+ } },
+ },
+ [EM2820_BOARD_MSI_VOX_USB_2] = {
+ .name = "MSI VOX USB 2.0",
+ .vchannels = 3,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .max_range_640_480 = 1,
+
+ .decoder = EM28XX_SAA7114,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE4,
+ .amux = 0,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = 1,
+ } },
},
[EM2800_BOARD_TERRATEC_CINERGY_200] = {
.name = "Terratec Cinergy 200 USB",
.is_em2800 = 1,
.vchannels = 3,
- .norm = VIDEO_MODE_PAL,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = 0,
- },{
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2800_BOARD_LEADTEK_WINFAST_USBII] = {
.name = "Leadtek Winfast USB II",
.is_em2800 = 1,
.vchannels = 3,
- .norm = VIDEO_MODE_PAL,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = 0,
- },{
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2800_BOARD_KWORLD_USB2800] = {
.name = "Kworld USB2800",
.is_em2800 = 1,
.vchannels = 3,
- .norm = VIDEO_MODE_PAL,
.tuner_type = TUNER_PHILIPS_ATSC,
.tda9887_conf = TDA9887_PRESENT,
- .has_tuner = 1,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = 0,
- },{
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
[EM2820_BOARD_PINNACLE_DVC_90] = {
- .name = "Pinnacle Dazzle DVC 90",
+ .name = "Pinnacle Dazzle DVC 90/DVC 100",
+ .vchannels = 3,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_SAA7113,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = 1,
+ } },
+ },
+ [EM2800_BOARD_VGEAR_POCKETTV] = {
+ .name = "V-Gear PocketTV",
+ .is_em2800 = 1,
+ .vchannels = 3,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA7113,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = 0,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = 1,
+ } },
+ },
+ [EM2820_BOARD_PROLINK_PLAYTV_USB2] = {
+ .name = "Pixelview Prolink PlayTV USB 2.0",
.vchannels = 3,
- .norm = VIDEO_MODE_PAL,
- .has_tuner = 0,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
.decoder = EM28XX_SAA7113,
- .input = {{
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = 1,
+ }, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
.amux = 1,
- },{
+ }, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
.amux = 1,
- }},
+ } },
},
};
const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
/* table of devices that work with this driver */
struct usb_device_id em28xx_id_table [] = {
- { USB_DEVICE(0xeb1a, 0x2800), .driver_info = EM2800_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2820), .driver_info = EM2820_BOARD_MSI_VOX_USB_2 },
- { USB_DEVICE(0x0ccd, 0x0036), .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 },
- { USB_DEVICE(0x2304, 0x0208), .driver_info = EM2820_BOARD_PINNACLE_USB_2 },
- { USB_DEVICE(0x2040, 0x4200), .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
- { USB_DEVICE(0x2304, 0x0207), .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0xeb1a, 0x2750),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2800),
+ .driver_info = EM2800_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2820),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2821),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2860),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2861),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2870),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2881),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2883),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0x0ccd, 0x0036),
+ .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 },
+ { USB_DEVICE(0x2304, 0x0208),
+ .driver_info = EM2820_BOARD_PINNACLE_USB_2 },
+ { USB_DEVICE(0x2040, 0x4200),
+ .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
+ { USB_DEVICE(0x2040, 0x4201),
+ .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
+ { USB_DEVICE(0x2304, 0x0207),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0x2304, 0x021a),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0x2040, 0x6500),
+ .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
+ { USB_DEVICE(0x2040, 0x6513),
+ .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+ { USB_DEVICE(0x0ccd, 0x0042),
+ .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS },
+ { USB_DEVICE(0x0ccd, 0x0047),
+ .driver_info = EM2880_BOARD_TERRATEC_PRODIGY_XS },
{ },
};
+MODULE_DEVICE_TABLE(usb, em28xx_id_table);
+
+/* EEPROM hash table for devices with generic USB IDs */
+static struct em28xx_hash_table em28xx_eeprom_hash [] = {
+ /* P/N: SA 60002070465 Tuner: TVF7533-MF */
+ {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
+};
+
+/* I2C devicelist hash table for devices with generic USB IDs */
+static struct em28xx_hash_table em28xx_i2c_hash[] = {
+ {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC},
+ {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC},
+};
+/* Since em28xx_pre_card_setup() requires a proper dev->model,
+ * this won't work for boards with generic PCI IDs
+ */
void em28xx_pre_card_setup(struct em28xx *dev)
{
/* request some modules */
- switch(dev->model){
- case EM2880_BOARD_TERRATEC_PRODIGY_XS:
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
- case EM2880_BOARD_TERRATEC_HYBRID_XS:
- {
- em28xx_write_regs_req(dev, 0x00, 0x08, "\x7d", 1); // reset through GPIO?
- break;
+ switch (dev->model) {
+ case EM2880_BOARD_TERRATEC_PRODIGY_XS:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ case EM2880_BOARD_TERRATEC_HYBRID_XS:
+ em28xx_write_regs(dev, XCLK_REG, "\x27", 1);
+ em28xx_write_regs(dev, I2C_CLK_REG, "\x40", 1);
+ em28xx_write_regs(dev, 0x08, "\xff", 1);
+ em28xx_write_regs(dev, 0x04, "\x00", 1);
+ msleep(100);
+ em28xx_write_regs(dev, 0x04, "\x08", 1);
+ msleep(100);
+ em28xx_write_regs(dev, 0x08, "\xff", 1);
+ msleep(50);
+ em28xx_write_regs(dev, 0x08, "\x2d", 1);
+ msleep(50);
+ em28xx_write_regs(dev, 0x08, "\x3d", 1);
+ break;
+ }
+}
+
+static int em28xx_tuner_callback(void *ptr, int command, int arg)
+{
+ int rc = 0;
+ struct em28xx *dev = ptr;
+
+ if (dev->tuner_type != TUNER_XC2028)
+ return 0;
+
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ {
+ /* GPIO and initialization codes for analog TV and radio
+ This code should be complemented for DTV, since reset
+ codes are different.
+ */
+
+ dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
+ dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1);
+
+ if (dev->analog_gpio) {
+ char gpio0 = dev->analog_gpio & 0xff;
+ char gpio1 = (dev->analog_gpio >> 8) & 0xff;
+ char gpio4 = dev->analog_gpio >> 24;
+
+ if (gpio4) {
+ dev->em28xx_write_regs(dev, 0x04, &gpio4, 1);
+ msleep(140);
}
+
+ msleep(6);
+ dev->em28xx_write_regs(dev, 0x08, &gpio0, 1);
+ msleep(10);
+ dev->em28xx_write_regs(dev, 0x08, &gpio1, 1);
+ msleep(5);
+ }
+
+ break;
+ }
+ }
+ return rc;
+}
+
+static void em28xx_config_tuner(struct em28xx *dev)
+{
+ struct v4l2_priv_tun_config xc2028_cfg;
+ struct xc2028_ctrl ctl;
+ struct tuner_setup tun_setup;
+ struct v4l2_frequency f;
+
+ if (dev->tuner_type == TUNER_ABSENT)
+ return;
+
+ tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = dev->tuner_addr;
+ tun_setup.tuner_callback = em28xx_tuner_callback;
+
+ em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
+
+ if (dev->tuner_type == TUNER_XC2028) {
+ memset(&ctl, 0, sizeof(ctl));
+
+ ctl.fname = XC2028_DEFAULT_FIRMWARE;
+ ctl.max_len = 64;
+ ctl.mts = em28xx_boards[dev->model].mts_firmware;
+
+ xc2028_cfg.tuner = TUNER_XC2028;
+ xc2028_cfg.priv = &ctl;
+
+ em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg);
+ }
+
+ /* configure tuner */
+ f.tuner = 0;
+ f.type = V4L2_TUNER_ANALOG_TV;
+ f.frequency = 9076; /* just a magic number */
+ dev->ctl_freq = f.frequency;
+ em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
+}
+
+static int em28xx_hint_board(struct em28xx *dev)
+{
+ int i;
+
+ /* HINT method: EEPROM
+ *
+ * This method works only for boards with eeprom.
+ * Uses a hash of all eeprom bytes. The hash should be
+ * unique for a vendor/tuner pair.
+ * There are a high chance that tuners for different
+ * video standards produce different hashes.
+ */
+ for (i = 0; i < ARRAY_SIZE(em28xx_eeprom_hash); i++) {
+ if (dev->hash == em28xx_eeprom_hash[i].hash) {
+ dev->model = em28xx_eeprom_hash[i].model;
+ dev->tuner_type = em28xx_eeprom_hash[i].tuner;
+
+ em28xx_errdev("Your board has no unique USB ID.\n");
+ em28xx_errdev("A hint were successfully done, "
+ "based on eeprom hash.\n");
+ em28xx_errdev("This method is not 100%% failproof.\n");
+ em28xx_errdev("If the board were missdetected, "
+ "please email this log to:\n");
+ em28xx_errdev("\tV4L Mailing List "
+ " <video4linux-list@redhat.com>\n");
+ em28xx_errdev("Board detected as %s\n",
+ em28xx_boards[dev->model].name);
+
+ return 0;
+ }
+ }
+
+ /* HINT method: I2C attached devices
+ *
+ * This method works for all boards.
+ * Uses a hash of i2c scanned devices.
+ * Devices with the same i2c attached chips will
+ * be considered equal.
+ * This method is less precise than the eeprom one.
+ */
+
+ /* user did not request i2c scanning => do it now */
+ if (!dev->i2c_hash)
+ em28xx_do_i2c_scan(dev);
+
+ for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) {
+ if (dev->i2c_hash == em28xx_i2c_hash[i].hash) {
+ dev->model = em28xx_i2c_hash[i].model;
+ dev->tuner_type = em28xx_i2c_hash[i].tuner;
+ em28xx_errdev("Your board has no unique USB ID.\n");
+ em28xx_errdev("A hint were successfully done, "
+ "based on i2c devicelist hash.\n");
+ em28xx_errdev("This method is not 100%% failproof.\n");
+ em28xx_errdev("If the board were missdetected, "
+ "please email this log to:\n");
+ em28xx_errdev("\tV4L Mailing List "
+ " <video4linux-list@redhat.com>\n");
+ em28xx_errdev("Board detected as %s\n",
+ em28xx_boards[dev->model].name);
+
+ return 0;
+ }
+ }
+
+ em28xx_errdev("Your board has no unique USB ID and thus need a "
+ "hint to be detected.\n");
+ em28xx_errdev("You may try to use card=<n> insmod option to "
+ "workaround that.\n");
+ em28xx_errdev("Please send an email with this log to:\n");
+ em28xx_errdev("\tV4L Mailing List <video4linux-list@redhat.com>\n");
+ em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash);
+ em28xx_errdev("Board i2c devicelist hash is 0x%08lx\n", dev->i2c_hash);
+
+ em28xx_errdev("Here is a list of valid choices for the card=<n>"
+ " insmod option:\n");
+ for (i = 0; i < em28xx_bcount; i++) {
+ em28xx_errdev(" card=%d -> %s\n",
+ i, em28xx_boards[i].name);
+ }
+ return -1;
+}
+
+
+static void em28xx_set_model(struct em28xx *dev)
+{
+ dev->is_em2800 = em28xx_boards[dev->model].is_em2800;
+ dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx;
+ dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf;
+ dev->decoder = em28xx_boards[dev->model].decoder;
+ dev->video_inputs = em28xx_boards[dev->model].vchannels;
+ dev->analog_gpio = em28xx_boards[dev->model].analog_gpio;
+ dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s;
+ dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480;
+}
+
+/* ----------------------------------------------------------------------- */
+void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir)
+{
+ if (disable_ir) {
+ ir->get_key = NULL;
+ return ;
+ }
+
+ /* detect & configure */
+ switch (dev->model) {
+ case (EM2800_BOARD_UNKNOWN):
+ break;
+ case (EM2820_BOARD_UNKNOWN):
+ break;
+ case (EM2800_BOARD_TERRATEC_CINERGY_200):
+ case (EM2820_BOARD_TERRATEC_CINERGY_250):
+ ir->ir_codes = ir_codes_em_terratec;
+ ir->get_key = em28xx_get_key_terratec;
+ snprintf(ir->c.name, sizeof(ir->c.name),
+ "i2c IR (EM28XX Terratec)");
+ break;
+ case (EM2820_BOARD_PINNACLE_USB_2):
+ ir->ir_codes = ir_codes_pinnacle_grey;
+ ir->get_key = em28xx_get_key_pinnacle_usb_grey;
+ snprintf(ir->c.name, sizeof(ir->c.name),
+ "i2c IR (EM28XX Pinnacle PCTV)");
+ break;
+ case (EM2820_BOARD_HAUPPAUGE_WINTV_USB_2):
+ ir->ir_codes = ir_codes_hauppauge_new;
+ ir->get_key = em28xx_get_key_em_haup;
+ snprintf(ir->c.name, sizeof(ir->c.name),
+ "i2c IR (EM2840 Hauppauge)");
+ break;
+ case (EM2820_BOARD_MSI_VOX_USB_2):
+ break;
+ case (EM2800_BOARD_LEADTEK_WINFAST_USBII):
+ break;
+ case (EM2800_BOARD_KWORLD_USB2800):
+ break;
}
}
void em28xx_card_setup(struct em28xx *dev)
{
+ em28xx_set_model(dev);
+
+ dev->tuner_type = em28xx_boards[dev->model].tuner_type;
+
/* request some modules */
- switch(dev->model){
- case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
- {
- struct tveeprom tv;
+ switch (dev->model) {
+ case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ {
+ struct tveeprom tv;
#ifdef CONFIG_MODULES
- request_module("tveeprom");
- request_module("ir-kbd-i2c");
- request_module("msp3400");
+ request_module("tveeprom");
#endif
- /* Call first TVeeprom */
-
- dev->i2c_client.addr = 0xa0 >> 1;
- tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata);
-
- dev->tuner_type= tv.tuner_type;
- if (tv.audio_processor == AUDIO_CHIP_MSP34XX) {
- dev->i2s_speed=2048000;
- dev->has_msp34xx=1;
- } else
- dev->has_msp34xx=0;
- break;
- }
- case EM2820_BOARD_KWORLD_PVRTV2800RF:
- {
- em28xx_write_regs_req(dev,0x00,0x08, "\xf9", 1); // GPIO enables sound on KWORLD PVR TV 2800RF
- break;
- }
+ /* Call first TVeeprom */
+
+ dev->i2c_client.addr = 0xa0 >> 1;
+ tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata);
+ dev->tuner_type = tv.tuner_type;
+
+ if (tv.audio_processor == AUDIO_CHIP_MSP34XX) {
+ dev->i2s_speed = 2048000;
+ dev->has_msp34xx = 1;
+ }
+#ifdef CONFIG_MODULES
+ if (tv.has_ir)
+ request_module("ir-kbd-i2c");
+#endif
+ break;
+ }
+ case EM2820_BOARD_KWORLD_PVRTV2800RF:
+ /* GPIO enables sound on KWORLD PVR TV 2800RF */
+ em28xx_write_regs_req(dev, 0x00, 0x08, "\xf9", 1);
+ break;
+ case EM2820_BOARD_UNKNOWN:
+ case EM2800_BOARD_UNKNOWN:
+ if (!em28xx_hint_board(dev))
+ em28xx_set_model(dev);
}
-}
-MODULE_DEVICE_TABLE (usb, em28xx_id_table);
+ /* Allow override tuner type by a module parameter */
+ if (tuner >= 0)
+ dev->tuner_type = tuner;
+
+#ifdef CONFIG_MODULES
+ /* request some modules */
+ if (dev->has_msp34xx)
+ request_module("msp3400");
+ if (dev->decoder == EM28XX_SAA7113 || dev->decoder == EM28XX_SAA7114)
+ request_module("saa7115");
+ if (dev->decoder == EM28XX_TVP5150)
+ request_module("tvp5150");
+ if (dev->tuner_type != TUNER_ABSENT)
+ request_module("tuner");
+#endif
+
+ em28xx_config_tuner(dev);
+}
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index d56484f20467..f6b78357f0e5 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -252,7 +252,7 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
* em28xx_write_ac97()
* write a 16 bit value to the specified AC97 address (LSB first!)
*/
-int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 * val)
+static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val)
{
int ret;
u8 addr = reg & 0x7f;
@@ -268,16 +268,98 @@ int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 * val)
return 0;
}
+int em28xx_set_audio_source(struct em28xx *dev)
+{
+ static char *enable = "\x08\x08";
+ static char *disable = "\x08\x88";
+ char *video = enable, *line = disable;
+ int ret, no_ac97;
+ u8 input;
+
+ if (dev->is_em2800) {
+ if (dev->ctl_ainput)
+ input = EM2800_AUDIO_SRC_LINE;
+ else
+ input = EM2800_AUDIO_SRC_TUNER;
+
+ ret = em28xx_write_regs(dev, EM2800_AUDIOSRC_REG, &input, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (dev->has_msp34xx)
+ input = EM28XX_AUDIO_SRC_TUNER;
+ else {
+ switch (dev->ctl_ainput) {
+ case EM28XX_AMUX_VIDEO:
+ input = EM28XX_AUDIO_SRC_TUNER;
+ no_ac97 = 1;
+ break;
+ case EM28XX_AMUX_LINE_IN:
+ input = EM28XX_AUDIO_SRC_LINE;
+ no_ac97 = 1;
+ break;
+ case EM28XX_AMUX_AC97_VIDEO:
+ input = EM28XX_AUDIO_SRC_LINE;
+ break;
+ case EM28XX_AMUX_AC97_LINE_IN:
+ input = EM28XX_AUDIO_SRC_LINE;
+ video = disable;
+ line = enable;
+ break;
+ }
+ }
+
+ ret = em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0);
+ if (ret < 0)
+ return ret;
+
+ if (no_ac97)
+ return 0;
+
+ /* Sets AC97 mixer registers */
+
+ ret = em28xx_write_ac97(dev, VIDEO_AC97, video);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_ac97(dev, LINE_IN_AC97, line);
+
+ return ret;
+}
+
int em28xx_audio_analog_set(struct em28xx *dev)
{
+ int ret;
char s[2] = { 0x00, 0x00 };
+ u8 xclk = 0x07;
+
s[0] |= 0x1f - dev->volume;
s[1] |= 0x1f - dev->volume;
+
if (dev->mute)
s[1] |= 0x80;
- return em28xx_write_ac97(dev, MASTER_AC97, s);
-}
+ ret = em28xx_write_ac97(dev, MASTER_AC97, s);
+ if (ret < 0)
+ return ret;
+
+ if (dev->has_12mhz_i2s)
+ xclk |= 0x20;
+
+ if (!dev->mute)
+ xclk |= 0x80;
+ ret = em28xx_write_reg_bits(dev, XCLK_REG, xclk, 0xa7);
+ if (ret < 0)
+ return ret;
+ msleep(10);
+
+ /* Selects the proper audio input */
+ ret = em28xx_set_audio_source(dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(em28xx_audio_analog_set);
int em28xx_colorlevels_set_default(struct em28xx *dev)
{
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index e3a4aa7a9df4..cacd04d46e99 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -25,9 +25,9 @@
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/i2c.h>
-#include <linux/video_decoder.h>
#include "em28xx.h"
+#include "tuner-xc2028.h"
#include <media/v4l2-common.h>
#include <media/tuner.h>
@@ -291,6 +291,31 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
return rc;
}
+/* based on linux/sunrpc/svcauth.h and linux/hash.h
+ * The original hash function returns a different value, if arch is x86_64
+ * or i386.
+ */
+static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits)
+{
+ unsigned long hash = 0;
+ unsigned long l = 0;
+ int len = 0;
+ unsigned char c;
+ do {
+ if (len == length) {
+ c = (char)len;
+ len = -1;
+ } else
+ c = *buf++;
+ l = (l << 8) | c;
+ len++;
+ if ((len & (32 / 8 - 1)) == 0)
+ hash = ((hash^l) * 0x9e370001UL);
+ } while (len);
+
+ return (hash >> (32 - bits)) & 0xffffffffUL;
+}
+
static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
{
unsigned char buf, *p = eedata;
@@ -334,7 +359,11 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
printk("\n");
}
- printk(KERN_INFO "EEPROM ID= 0x%08x\n", em_eeprom->id);
+ if (em_eeprom->id == 0x9567eb1a)
+ dev->hash = em28xx_hash_mem(eedata, len, 32);
+
+ printk(KERN_INFO "EEPROM ID= 0x%08x, hash = 0x%08lx\n",
+ em_eeprom->id, dev->hash);
printk(KERN_INFO "Vendor/Product ID= %04x:%04x\n", em_eeprom->vendor_ID,
em_eeprom->product_ID);
@@ -391,21 +420,6 @@ static u32 functionality(struct i2c_adapter *adap)
}
-static int em28xx_set_tuner(int check_eeprom, struct i2c_client *client)
-{
- struct em28xx *dev = client->adapter->algo_data;
- struct tuner_setup tun_setup;
-
- if (dev->has_tuner) {
- tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
- tun_setup.type = dev->tuner_type;
- tun_setup.addr = dev->tuner_addr;
- em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
- }
-
- return (0);
-}
-
/*
* attach_inform()
* gets called when a device attaches to the i2c bus
@@ -421,6 +435,8 @@ static int attach_inform(struct i2c_client *client)
case 0x96:
case 0x94:
{
+ struct v4l2_priv_tun_config tda9887_cfg;
+
struct tuner_setup tun_setup;
tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
@@ -428,7 +444,11 @@ static int attach_inform(struct i2c_client *client)
tun_setup.addr = client->addr;
em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
- em28xx_i2c_call_clients(dev, TDA9887_SET_CONFIG, &dev->tda9887_conf);
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &dev->tda9887_conf;
+ em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG,
+ &tda9887_cfg);
break;
}
case 0x42:
@@ -458,9 +478,11 @@ static int attach_inform(struct i2c_client *client)
break;
default:
+ if (!dev->tuner_addr)
+ dev->tuner_addr = client->addr;
+
dprintk1(1,"attach inform: detected I2C address %x\n", client->addr << 1);
- dev->tuner_addr = client->addr;
- em28xx_set_tuner(-1, client);
+
}
return 0;
@@ -510,19 +532,26 @@ static char *i2c_devs[128] = {
* do_i2c_scan()
* check i2c address range for devices
*/
-static void do_i2c_scan(char *name, struct i2c_client *c)
+void em28xx_do_i2c_scan(struct em28xx *dev)
{
+ u8 i2c_devicelist[128];
unsigned char buf;
int i, rc;
+ memset(i2c_devicelist, 0, ARRAY_SIZE(i2c_devicelist));
+
for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
- c->addr = i;
- rc = i2c_master_recv(c, &buf, 0);
+ dev->i2c_client.addr = i;
+ rc = i2c_master_recv(&dev->i2c_client, &buf, 0);
if (rc < 0)
continue;
- printk(KERN_INFO "%s: found i2c device @ 0x%x [%s]\n", name,
- i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ i2c_devicelist[i] = i;
+ printk(KERN_INFO "%s: found i2c device @ 0x%x [%s]\n",
+ dev->name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
}
+
+ dev->i2c_hash = em28xx_hash_mem(i2c_devicelist,
+ ARRAY_SIZE(i2c_devicelist), 32);
}
/*
@@ -555,7 +584,7 @@ int em28xx_i2c_register(struct em28xx *dev)
em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata));
if (i2c_scan)
- do_i2c_scan(dev->name, &dev->i2c_client);
+ em28xx_do_i2c_scan(dev);
return 0;
}
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index e3894b68c4ee..10da2fd8d987 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -30,11 +30,7 @@
#include "em28xx.h"
-static unsigned int disable_ir = 0;
-module_param(disable_ir, int, 0444);
-MODULE_PARM_DESC(disable_ir,"disable infrared remote support");
-
-static unsigned int ir_debug = 0;
+static unsigned int ir_debug;
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]");
@@ -43,7 +39,7 @@ MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]");
/* ----------------------------------------------------------------------- */
-static int get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
unsigned char b;
@@ -72,7 +68,7 @@ static int get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
}
-static int get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
unsigned char buf[2];
unsigned char code;
@@ -103,7 +99,8 @@ static int get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
return 1;
}
-static int get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw)
{
unsigned char buf[3];
@@ -125,45 +122,6 @@ static int get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw
return 1;
}
-/* ----------------------------------------------------------------------- */
-void em28xx_set_ir(struct em28xx * dev,struct IR_i2c *ir)
-{
- if (disable_ir) {
- ir->get_key=NULL;
- return ;
- }
-
- /* detect & configure */
- switch (dev->model) {
- case (EM2800_BOARD_UNKNOWN):
- break;
- case (EM2820_BOARD_UNKNOWN):
- break;
- case (EM2800_BOARD_TERRATEC_CINERGY_200):
- case (EM2820_BOARD_TERRATEC_CINERGY_250):
- ir->ir_codes = ir_codes_em_terratec;
- ir->get_key = get_key_terratec;
- snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (EM28XX Terratec)");
- break;
- case (EM2820_BOARD_PINNACLE_USB_2):
- ir->ir_codes = ir_codes_pinnacle_grey;
- ir->get_key = get_key_pinnacle_usb_grey;
- snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (EM28XX Pinnacle PCTV)");
- break;
- case (EM2820_BOARD_HAUPPAUGE_WINTV_USB_2):
- ir->ir_codes = ir_codes_hauppauge_new;
- ir->get_key = get_key_em_haup;
- snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (EM2840 Hauppauge)");
- break;
- case (EM2820_BOARD_MSI_VOX_USB_2):
- break;
- case (EM2800_BOARD_LEADTEK_WINFAST_USBII):
- break;
- case (EM2800_BOARD_KWORLD_USB2800):
- break;
- }
-}
-
/* ----------------------------------------------------------------------
* Local variables:
* c-basic-offset: 8
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 0906bc5766cc..a0c334672488 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -33,13 +33,12 @@
#include <linux/i2c.h>
#include <linux/version.h>
#include <linux/mm.h>
-#include <linux/video_decoder.h>
#include <linux/mutex.h>
#include "em28xx.h"
-#include <media/tuner.h>
#include <media/v4l2-common.h>
#include <media/msp3400.h>
+#include <media/tuner.h>
#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
"Markus Rechberger <mrechberger@gmail.com>, " \
@@ -48,7 +47,7 @@
#define DRIVER_NAME "em28xx"
#define DRIVER_DESC "Empia em28xx based USB video device driver"
-#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 0, 1)
+#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 0)
#define em28xx_videodbg(fmt, arg...) do {\
if (video_debug) \
@@ -63,17 +62,17 @@ static LIST_HEAD(em28xx_devlist);
static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
-static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+
module_param_array(card, int, NULL, 0444);
module_param_array(video_nr, int, NULL, 0444);
module_param_array(vbi_nr, int, NULL, 0444);
-MODULE_PARM_DESC(card,"card type");
-MODULE_PARM_DESC(video_nr,"video device numbers");
-MODULE_PARM_DESC(vbi_nr,"vbi device numbers");
-
-static int tuner = -1;
-module_param(tuner, int, 0444);
-MODULE_PARM_DESC(tuner, "tuner type");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
static unsigned int video_debug = 0;
module_param(video_debug,int,0644);
@@ -82,29 +81,6 @@ MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
static unsigned long em28xx_devused;
-/* supported tv norms */
-static struct em28xx_tvnorm tvnorms[] = {
- {
- .name = "PAL",
- .id = V4L2_STD_PAL,
- .mode = VIDEO_MODE_PAL,
- }, {
- .name = "NTSC",
- .id = V4L2_STD_NTSC,
- .mode = VIDEO_MODE_NTSC,
- }, {
- .name = "SECAM",
- .id = V4L2_STD_SECAM,
- .mode = VIDEO_MODE_SECAM,
- }, {
- .name = "PAL-M",
- .id = V4L2_STD_PAL_M,
- .mode = VIDEO_MODE_PAL,
- }
-};
-
-#define TVNORMS ARRAY_SIZE(tvnorms)
-
/* supported controls */
/* Common to all boards */
static struct v4l2_queryctrl em28xx_qctrl[] = {
@@ -131,8 +107,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
static struct usb_driver em28xx_usb_driver;
-static DEFINE_MUTEX(em28xx_sysfs_lock);
-static DECLARE_RWSEM(em28xx_disconnect);
/********************* v4l2 interface ******************************************/
@@ -153,11 +127,9 @@ static int em28xx_config(struct em28xx *dev)
/* em28xx_write_regs_req(dev,0x00,0x0f,"\x80",1); clk register */
em28xx_write_regs_req(dev,0x00,0x11,"\x51",1);
- em28xx_audio_usb_mute(dev, 1);
dev->mute = 1; /* maybe not the right place... */
dev->volume = 0x1f;
- em28xx_audio_analog_set(dev);
- em28xx_audio_analog_setup(dev);
+
em28xx_outfmt_set_yuv422(dev);
em28xx_colorlevels_set_default(dev);
em28xx_compression_disable(dev);
@@ -171,7 +143,6 @@ static int em28xx_config(struct em28xx *dev)
*/
static void em28xx_config_i2c(struct em28xx *dev)
{
- struct v4l2_frequency f;
struct v4l2_routing route;
route.input = INPUT(dev->ctl_input)->vmux;
@@ -179,18 +150,6 @@ static void em28xx_config_i2c(struct em28xx *dev)
em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, NULL);
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
-
- /* configure tuner */
- f.tuner = 0;
- f.type = V4L2_TUNER_ANALOG_TV;
- f.frequency = 9076; /* FIXME:remove magic number */
- dev->ctl_freq = f.frequency;
- em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
-
- /* configure tda9887 */
-
-
-/* em28xx_i2c_call_clients(dev,VIDIOC_S_STD,&dev->tvnorm->id); */
}
/*
@@ -212,7 +171,6 @@ static void em28xx_empty_framequeues(struct em28xx *dev)
static void video_mux(struct em28xx *dev, int index)
{
- int ainput;
struct v4l2_routing route;
route.input = INPUT(index)->vmux;
@@ -222,8 +180,6 @@ static void video_mux(struct em28xx *dev, int index)
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
- em28xx_videodbg("Setting input index=%d, vmux=%d, amux=%d\n",index,route.input,dev->ctl_ainput);
-
if (dev->has_msp34xx) {
if (dev->i2s_speed)
em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed);
@@ -231,18 +187,1068 @@ static void video_mux(struct em28xx *dev, int index)
route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
/* Note: this is msp3400 specific */
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route);
- ainput = EM28XX_AUDIO_SRC_TUNER;
- em28xx_audio_source(dev, ainput);
+ }
+
+ em28xx_set_audio_source(dev);
+}
+
+/* Usage lock check functions */
+static int res_get(struct em28xx_fh *fh)
+{
+ struct em28xx *dev = fh->dev;
+ int rc = 0;
+
+ /* This instance already has stream_on */
+ if (fh->stream_on)
+ return rc;
+
+ mutex_lock(&dev->lock);
+
+ if (dev->stream_on)
+ rc = -EINVAL;
+ else {
+ dev->stream_on = 1;
+ fh->stream_on = 1;
+ }
+
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int res_check(struct em28xx_fh *fh)
+{
+ return (fh->stream_on);
+}
+
+static void res_free(struct em28xx_fh *fh)
+{
+ struct em28xx *dev = fh->dev;
+
+ mutex_lock(&dev->lock);
+ fh->stream_on = 0;
+ dev->stream_on = 0;
+ mutex_unlock(&dev->lock);
+}
+
+/*
+ * em28xx_vm_open()
+ */
+static void em28xx_vm_open(struct vm_area_struct *vma)
+{
+ struct em28xx_frame_t *f = vma->vm_private_data;
+ f->vma_use_count++;
+}
+
+/*
+ * em28xx_vm_close()
+ */
+static void em28xx_vm_close(struct vm_area_struct *vma)
+{
+ /* NOTE: buffers are not freed here */
+ struct em28xx_frame_t *f = vma->vm_private_data;
+
+ if (f->vma_use_count)
+ f->vma_use_count--;
+}
+
+static struct vm_operations_struct em28xx_vm_ops = {
+ .open = em28xx_vm_open,
+ .close = em28xx_vm_close,
+};
+
+
+/*
+ * em28xx_get_ctrl()
+ * return the current saturation, brightness or contrast, mute state
+ */
+static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = dev->mute;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value = dev->volume;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * em28xx_set_ctrl()
+ * mute or set new saturation, brightness or contrast
+ */
+static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value != dev->mute) {
+ dev->mute = ctrl->value;
+ return em28xx_audio_analog_set(dev);
+ }
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ dev->volume = ctrl->value;
+ return em28xx_audio_analog_set(dev);
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * em28xx_stream_interrupt()
+ * stops streaming
+ */
+static int em28xx_stream_interrupt(struct em28xx *dev)
+{
+ int rc = 0;
+
+ /* stop reading from the device */
+
+ dev->stream = STREAM_INTERRUPT;
+ rc = wait_event_timeout(dev->wait_stream,
+ (dev->stream == STREAM_OFF) ||
+ (dev->state & DEV_DISCONNECTED),
+ EM28XX_URB_TIMEOUT);
+
+ if (rc) {
+ dev->state |= DEV_MISCONFIGURED;
+ em28xx_videodbg("device is misconfigured; close and "
+ "open /dev/video%d again\n",
+ dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
+ return rc;
+ }
+
+ return 0;
+}
+
+
+static int check_dev(struct em28xx *dev)
+{
+ if (dev->state & DEV_DISCONNECTED) {
+ em28xx_errdev("v4l2 ioctl: device not present\n");
+ return -ENODEV;
+ }
+
+ if (dev->state & DEV_MISCONFIGURED) {
+ em28xx_errdev("v4l2 ioctl: device is misconfigured; "
+ "close and open it again\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static void get_scale(struct em28xx *dev,
+ unsigned int width, unsigned int height,
+ unsigned int *hscale, unsigned int *vscale)
+{
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+
+ *hscale = (((unsigned long)maxw) << 12) / width - 4096L;
+ if (*hscale >= 0x4000)
+ *hscale = 0x3fff;
+
+ *vscale = (((unsigned long)maxh) << 12) / height - 4096L;
+ if (*vscale >= 0x4000)
+ *vscale = 0x3fff;
+}
+
+/* ------------------------------------------------------------------
+ IOCTL vidioc handling
+ ------------------------------------------------------------------*/
+
+static int vidioc_g_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ mutex_lock(&dev->lock);
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ f->fmt.pix.bytesperline = dev->bytesperline;
+ f->fmt.pix.sizeimage = dev->frame_size;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
+ f->fmt.pix.field = dev->interlaced ?
+ V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_try_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int width = f->fmt.pix.width;
+ int height = f->fmt.pix.height;
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+ unsigned int hscale, vscale;
+
+ /* width must even because of the YUYV format
+ height must be even because of interlacing */
+ height &= 0xfffe;
+ width &= 0xfffe;
+
+ if (height < 32)
+ height = 32;
+ if (height > maxh)
+ height = maxh;
+ if (width < 48)
+ width = 48;
+ if (width > maxw)
+ width = maxw;
+
+ mutex_lock(&dev->lock);
+
+ if (dev->is_em2800) {
+ /* the em2800 can only scale down to 50% */
+ if (height % (maxh / 2))
+ height = maxh;
+ if (width % (maxw / 2))
+ width = maxw;
+ /* according to empiatech support */
+ /* the MaxPacketSize is to small to support */
+ /* framesizes larger than 640x480 @ 30 fps */
+ /* or 640x576 @ 25 fps. As this would cut */
+ /* of a part of the image we prefer */
+ /* 360x576 or 360x480 for now */
+ if (width == maxw && height == maxh)
+ width /= 2;
+ }
+
+ get_scale(dev, width, height, &hscale, &vscale);
+
+ width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
+ height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
+
+ f->fmt.pix.width = width;
+ f->fmt.pix.height = height;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ f->fmt.pix.bytesperline = width * 2;
+ f->fmt.pix.sizeimage = width * 2 * height;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc, i;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ vidioc_try_fmt_cap(file, priv, f);
+
+ mutex_lock(&dev->lock);
+
+ for (i = 0; i < dev->num_frames; i++)
+ if (dev->frame[i].vma_use_count) {
+ em28xx_videodbg("VIDIOC_S_FMT failed. "
+ "Unmap the buffers first.\n");
+ rc = -EINVAL;
+ goto err;
+ }
+
+ /* stop io in case it is already in progress */
+ if (dev->stream == STREAM_ON) {
+ em28xx_videodbg("VIDIOC_SET_FMT: interrupting stream\n");
+ rc = em28xx_stream_interrupt(dev);
+ if (rc < 0)
+ goto err;
+ }
+
+ em28xx_release_buffers(dev);
+ dev->io = IO_NONE;
+
+ /* set new image size */
+ dev->width = f->fmt.pix.width;
+ dev->height = f->fmt.pix.height;
+ dev->frame_size = dev->width * dev->height * 2;
+ dev->field_size = dev->frame_size >> 1;
+ dev->bytesperline = dev->width * 2;
+ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
+
+ /* FIXME: This is really weird! Why capture is starting with
+ this ioctl ???
+ */
+ em28xx_uninit_isoc(dev);
+ em28xx_set_alternate(dev);
+ em28xx_capture_start(dev, 1);
+ em28xx_resolution_set(dev);
+ em28xx_init_isoc(dev);
+ rc = 0;
+
+err:
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ struct v4l2_format f;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&dev->lock);
+ dev->norm = *norm;
+ mutex_unlock(&dev->lock);
+
+ /* Adjusts width/height, if needed */
+ f.fmt.pix.width = dev->width;
+ f.fmt.pix.height = dev->height;
+ vidioc_try_fmt_cap(file, priv, &f);
+
+ mutex_lock(&dev->lock);
+
+ /* set new image size */
+ dev->width = f.fmt.pix.width;
+ dev->height = f.fmt.pix.height;
+ dev->frame_size = dev->width * dev->height * 2;
+ dev->field_size = dev->frame_size >> 1;
+ dev->bytesperline = dev->width * 2;
+ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
+
+ em28xx_resolution_set(dev);
+ em28xx_i2c_call_clients(dev, VIDIOC_S_STD, &dev->norm);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static const char *iname[] = {
+ [EM28XX_VMUX_COMPOSITE1] = "Composite1",
+ [EM28XX_VMUX_COMPOSITE2] = "Composite2",
+ [EM28XX_VMUX_COMPOSITE3] = "Composite3",
+ [EM28XX_VMUX_COMPOSITE4] = "Composite4",
+ [EM28XX_VMUX_SVIDEO] = "S-Video",
+ [EM28XX_VMUX_TELEVISION] = "Television",
+ [EM28XX_VMUX_CABLE] = "Cable TV",
+ [EM28XX_VMUX_DVB] = "DVB",
+ [EM28XX_VMUX_DEBUG] = "for debug only",
+};
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= MAX_EM28XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ strcpy(i->name, iname[INPUT(n)->type]);
+
+ if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
+ (EM28XX_VMUX_CABLE == INPUT(n)->type))
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ i->std = dev->vdev->tvnorms;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ *i = dev->ctl_input;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (i >= MAX_EM28XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(i)->type)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ video_mux(dev, i);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ unsigned int index = a->index;
+
+ if (a->index > 1)
+ return -EINVAL;
+
+ index = dev->ctl_ainput;
+
+ if (index == 0) {
+ strcpy(a->name, "Television");
} else {
- switch (dev->ctl_ainput) {
- case 0:
- ainput = EM28XX_AUDIO_SRC_TUNER;
+ strcpy(a->name, "Line In");
+ }
+ a->capability = V4L2_AUDCAP_STEREO;
+ a->index = index;
+
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ if (a->index != dev->ctl_ainput)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int id = qc->id;
+ int i;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ memset(qc, 0, sizeof(*qc));
+
+ qc->id = id;
+
+ if (!dev->has_msp34xx) {
+ for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
+ if (qc->id && qc->id == em28xx_qctrl[i].id) {
+ memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+ }
+ mutex_lock(&dev->lock);
+ em28xx_i2c_call_clients(dev, VIDIOC_QUERYCTRL, qc);
+ mutex_unlock(&dev->lock);
+
+ if (qc->type)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+ mutex_lock(&dev->lock);
+
+ if (!dev->has_msp34xx)
+ rc = em28xx_get_ctrl(dev, ctrl);
+ else
+ rc = -EINVAL;
+
+ if (rc == -EINVAL) {
+ em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl);
+ rc = 0;
+ }
+
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ u8 i;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&dev->lock);
+
+ if (dev->has_msp34xx)
+ em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
+ else {
+ rc = 1;
+ for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
+ if (ctrl->id == em28xx_qctrl[i].id) {
+ if (ctrl->value < em28xx_qctrl[i].minimum ||
+ ctrl->value > em28xx_qctrl[i].maximum) {
+ rc = -ERANGE;
+ break;
+ }
+
+ rc = em28xx_set_ctrl(dev, ctrl);
break;
- default:
- ainput = EM28XX_AUDIO_SRC_LINE;
+ }
+ }
+ }
+
+ /* Control not found - try to send it to the attached devices */
+ if (rc == 1) {
+ em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
+ rc = 0;
+ }
+
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Tuner");
+
+ mutex_lock(&dev->lock);
+
+ em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->ctl_freq;
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != f->tuner)
+ return -EINVAL;
+
+ if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ dev->ctl_freq = f->frequency;
+ em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cc)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cc->bounds.left = 0;
+ cc->bounds.top = 0;
+ cc->bounds.width = dev->width;
+ cc->bounds.height = dev->height;
+ cc->defrect = cc->bounds;
+ cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
+ cc->pixelaspect.denominator = 59;
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&dev->inqueue))
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ if (unlikely(res_get(fh) < 0)) {
+ mutex_unlock(&dev->lock);
+ return -EBUSY;
+ }
+
+ dev->stream = STREAM_ON; /* FIXME: Start video capture here? */
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ if (dev->stream == STREAM_ON) {
+ em28xx_videodbg("VIDIOC_STREAMOFF: interrupting stream\n");
+ rc = em28xx_stream_interrupt(dev);
+ if (rc < 0) {
+ mutex_unlock(&dev->lock);
+ return rc;
+ }
+ }
+
+ em28xx_empty_framequeues(dev);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+ strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+ strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
+
+ cap->version = EM28XX_VERSION_CODE;
+
+ cap->capabilities =
+ V4L2_CAP_SLICED_VBI_CAPTURE |
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+
+ if (dev->tuner_type != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmtd)
+{
+ if (fmtd->index != 0)
+ return -EINVAL;
+
+ fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strcpy(fmtd->description, "Packed YUY2");
+ fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
+ memset(fmtd->reserved, 0, sizeof(fmtd->reserved));
+
+ return 0;
+}
+
+/* Sliced VBI ioctls */
+static int vidioc_g_fmt_vbi_capture(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&dev->lock);
+
+ f->fmt.sliced.service_set = 0;
+
+ em28xx_i2c_call_clients(dev, VIDIOC_G_FMT, f);
+
+ if (f->fmt.sliced.service_set == 0)
+ rc = -EINVAL;
+
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int vidioc_try_set_vbi_capture(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&dev->lock);
+ em28xx_i2c_call_clients(dev, VIDIOC_G_FMT, f);
+ mutex_unlock(&dev->lock);
+
+ if (f->fmt.sliced.service_set == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ u32 i;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ rb->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (dev->io == IO_READ) {
+ em28xx_videodbg("method is set to read;"
+ " close and open the device again to"
+ " choose the mmap I/O method\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < dev->num_frames; i++)
+ if (dev->frame[i].vma_use_count) {
+ em28xx_videodbg("VIDIOC_REQBUFS failed; "
+ "previous buffers are still mapped\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->lock);
+
+ if (dev->stream == STREAM_ON) {
+ em28xx_videodbg("VIDIOC_REQBUFS: interrupting stream\n");
+ rc = em28xx_stream_interrupt(dev);
+ if (rc < 0) {
+ mutex_unlock(&dev->lock);
+ return rc;
}
- em28xx_audio_source(dev, ainput);
}
+
+ em28xx_empty_framequeues(dev);
+
+ em28xx_release_buffers(dev);
+ if (rb->count)
+ rb->count = em28xx_request_buffers(dev, rb->count);
+
+ dev->frame_current = NULL;
+ dev->io = rb->count ? IO_MMAP : IO_NONE;
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b->index >= dev->num_frames || dev->io != IO_MMAP)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ memcpy(b, &dev->frame[b->index].buf, sizeof(*b));
+
+ if (dev->frame[b->index].vma_use_count)
+ b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (dev->frame[b->index].state == F_DONE)
+ b->flags |= V4L2_BUF_FLAG_DONE;
+ else if (dev->frame[b->index].state != F_UNUSED)
+ b->flags |= V4L2_BUF_FLAG_QUEUED;
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ unsigned long lock_flags;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP ||
+ b->index >= dev->num_frames)
+ return -EINVAL;
+
+ if (dev->frame[b->index].state != F_UNUSED)
+ return -EAGAIN;
+
+ dev->frame[b->index].state = F_QUEUED;
+
+ /* add frame to fifo */
+ spin_lock_irqsave(&dev->queue_lock, lock_flags);
+ list_add_tail(&dev->frame[b->index].frame, &dev->inqueue);
+ spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
+
+ return 0;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+ struct em28xx_frame_t *f;
+ unsigned long lock_flags;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&dev->outqueue)) {
+ if (dev->stream == STREAM_OFF)
+ return -EINVAL;
+
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ rc = wait_event_interruptible(dev->wait_frame,
+ (!list_empty(&dev->outqueue)) ||
+ (dev->state & DEV_DISCONNECTED));
+ if (rc)
+ return rc;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&dev->queue_lock, lock_flags);
+ f = list_entry(dev->outqueue.next, struct em28xx_frame_t, frame);
+ list_del(dev->outqueue.next);
+ spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
+
+ f->state = F_UNUSED;
+ memcpy(b, &f->buf, sizeof(*b));
+
+ if (f->vma_use_count)
+ b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+/* RADIO ESPECIFIC IOCTLS */
+/* ----------------------------------------------------------- */
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+ strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+ strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+ strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
+
+ cap->version = EM28XX_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+ if (unlikely(t->index > 0))
+ return -EINVAL;
+
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
+
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *fh,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ if (qc->id < V4L2_CID_BASE ||
+ qc->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
+ if (qc->id && qc->id == em28xx_qctrl[i].id) {
+ memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+
+ return -EINVAL;
}
/*
@@ -252,8 +1258,9 @@ static void video_mux(struct em28xx *dev, int index)
static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
{
int minor = iminor(inode);
- int errCode = 0;
+ int errCode = 0, radio = 0;
struct em28xx *h,*dev = NULL;
+ struct em28xx_fh *fh;
list_for_each_entry(h, &em28xx_devlist, devlist) {
if (h->vdev->minor == minor) {
@@ -264,6 +1271,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
dev = h;
dev->type = V4L2_BUF_TYPE_VBI_CAPTURE;
}
+ if (h->radio_dev &&
+ h->radio_dev->minor == minor) {
+ radio = 1;
+ dev = h;
+ }
}
if (NULL == dev)
return -ENODEV;
@@ -271,23 +1283,18 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
em28xx_videodbg("open minor=%d type=%s users=%d\n",
minor,v4l2_type_names[dev->type],dev->users);
- if (!down_read_trylock(&em28xx_disconnect))
- return -ERESTARTSYS;
+ fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
- if (dev->users) {
- em28xx_warn("this driver can be opened only once\n");
- up_read(&em28xx_disconnect);
- return -EBUSY;
+ if (!fh) {
+ em28xx_errdev("em28xx-video.c: Out of memory?!\n");
+ return -ENOMEM;
}
-
- mutex_init(&dev->fileop_lock); /* to 1 == available */
- spin_lock_init(&dev->queue_lock);
- init_waitqueue_head(&dev->wait_frame);
- init_waitqueue_head(&dev->wait_stream);
-
mutex_lock(&dev->lock);
+ fh->dev = dev;
+ fh->radio = radio;
+ filp->private_data = fh;
- if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
em28xx_set_alternate(dev);
dev->width = norm_maxw(dev);
@@ -301,30 +1308,23 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
em28xx_capture_start(dev, 1);
em28xx_resolution_set(dev);
- /* device needs to be initialized before isoc transfer */
- video_mux(dev, 0);
/* start the transfer */
errCode = em28xx_init_isoc(dev);
if (errCode)
goto err;
+ em28xx_empty_framequeues(dev);
+ }
+ if (fh->radio) {
+ em28xx_videodbg("video_open: setting radio device\n");
+ em28xx_i2c_call_clients(dev, AUDC_SET_RADIO, NULL);
}
dev->users++;
- filp->private_data = dev;
- dev->io = IO_NONE;
- dev->stream = STREAM_OFF;
- dev->num_frames = 0;
-
- /* prepare queues */
- em28xx_empty_framequeues(dev);
-
- dev->state |= DEV_INITIALIZED;
err:
mutex_unlock(&dev->lock);
- up_read(&em28xx_disconnect);
return errCode;
}
@@ -335,7 +1335,6 @@ err:
*/
static void em28xx_release_resources(struct em28xx *dev)
{
- mutex_lock(&em28xx_sysfs_lock);
/*FIXME: I2C IR should be disconnected */
@@ -343,12 +1342,29 @@ static void em28xx_release_resources(struct em28xx *dev)
dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
list_del(&dev->devlist);
- video_unregister_device(dev->vdev);
- video_unregister_device(dev->vbi_dev);
+ if (dev->radio_dev) {
+ if (-1 != dev->radio_dev->minor)
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+ if (dev->vbi_dev) {
+ if (-1 != dev->vbi_dev->minor)
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ }
+ if (dev->vdev) {
+ if (-1 != dev->vdev->minor)
+ video_unregister_device(dev->vdev);
+ else
+ video_device_release(dev->vdev);
+ dev->vdev = NULL;
+ }
em28xx_i2c_unregister(dev);
usb_put_dev(dev->udev);
- mutex_unlock(&em28xx_sysfs_lock);
-
/* Mark device as unused */
em28xx_devused&=~(1<<dev->devno);
@@ -360,34 +1376,42 @@ static void em28xx_release_resources(struct em28xx *dev)
*/
static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
{
- int errCode;
- struct em28xx *dev=filp->private_data;
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ int errCode;
em28xx_videodbg("users=%d\n", dev->users);
- mutex_lock(&dev->lock);
- em28xx_uninit_isoc(dev);
+ if (res_check(fh))
+ res_free(fh);
- em28xx_release_buffers(dev);
+ mutex_lock(&dev->lock);
- /* the device is already disconnect, free the remaining resources */
- if (dev->state & DEV_DISCONNECTED) {
- em28xx_release_resources(dev);
- mutex_unlock(&dev->lock);
- kfree(dev);
- return 0;
- }
+ if (dev->users == 1) {
+ em28xx_uninit_isoc(dev);
+ em28xx_release_buffers(dev);
+ dev->io = IO_NONE;
- /* set alternate 0 */
- dev->alt = 0;
- em28xx_videodbg("setting alternate 0\n");
- errCode = usb_set_interface(dev->udev, 0, 0);
- if (errCode < 0) {
- em28xx_errdev ("cannot change alternate number to 0 (error=%i)\n",
- errCode);
- }
+ /* the device is already disconnect,
+ free the remaining resources */
+ if (dev->state & DEV_DISCONNECTED) {
+ em28xx_release_resources(dev);
+ mutex_unlock(&dev->lock);
+ kfree(dev);
+ return 0;
+ }
+ /* set alternate 0 */
+ dev->alt = 0;
+ em28xx_videodbg("setting alternate 0\n");
+ errCode = usb_set_interface(dev->udev, 0, 0);
+ if (errCode < 0) {
+ em28xx_errdev("cannot change alternate number to "
+ "0 (error=%i)\n", errCode);
+ }
+ }
+ kfree(fh);
dev->users--;
wake_up_interruptible_nr(&dev->open, 1);
mutex_unlock(&dev->lock);
@@ -405,56 +1429,65 @@ em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
struct em28xx_frame_t *f, *i;
unsigned long lock_flags;
int ret = 0;
- struct em28xx *dev = filp->private_data;
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
- if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ /* FIXME: read() is not prepared to allow changing the video
+ resolution while streaming. Seems a bug at em28xx_set_fmt
+ */
+
+ if (unlikely(res_get(fh) < 0))
+ return -EBUSY;
+
+ mutex_lock(&dev->lock);
+
+ if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
em28xx_videodbg("V4l2_Buf_type_videocapture is set\n");
- }
+
if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n");
em28xx_videodbg("not supported yet! ...\n");
if (copy_to_user(buf, "", 1)) {
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return -EFAULT;
}
+ mutex_unlock(&dev->lock);
return (1);
}
if (dev->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
em28xx_videodbg("V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set\n");
em28xx_videodbg("not supported yet! ...\n");
if (copy_to_user(buf, "", 1)) {
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return -EFAULT;
}
+ mutex_unlock(&dev->lock);
return (1);
}
- if (mutex_lock_interruptible(&dev->fileop_lock))
- return -ERESTARTSYS;
-
if (dev->state & DEV_DISCONNECTED) {
em28xx_videodbg("device not present\n");
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return -ENODEV;
}
if (dev->state & DEV_MISCONFIGURED) {
em28xx_videodbg("device misconfigured; close and open it again\n");
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return -EIO;
}
if (dev->io == IO_MMAP) {
em28xx_videodbg ("IO method is set to mmap; close and open"
" the device again to choose the read method\n");
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return -EINVAL;
}
if (dev->io == IO_NONE) {
if (!em28xx_request_buffers(dev, EM28XX_NUM_READ_FRAMES)) {
em28xx_errdev("read failed, not enough memory\n");
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return -ENOMEM;
}
dev->io = IO_READ;
@@ -463,13 +1496,13 @@ em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
}
if (!count) {
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return 0;
}
if (list_empty(&dev->outqueue)) {
if (filp->f_flags & O_NONBLOCK) {
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return -EAGAIN;
}
ret = wait_event_interruptible
@@ -477,35 +1510,46 @@ em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
(!list_empty(&dev->outqueue)) ||
(dev->state & DEV_DISCONNECTED));
if (ret) {
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return ret;
}
if (dev->state & DEV_DISCONNECTED) {
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return -ENODEV;
}
+ dev->video_bytesread = 0;
}
f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame);
- spin_lock_irqsave(&dev->queue_lock, lock_flags);
- list_for_each_entry(i, &dev->outqueue, frame)
- i->state = F_UNUSED;
- INIT_LIST_HEAD(&dev->outqueue);
- spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
-
em28xx_queue_unusedframes(dev);
if (count > f->buf.length)
count = f->buf.length;
- if (copy_to_user(buf, f->bufmem, count)) {
- mutex_unlock(&dev->fileop_lock);
+ if ((dev->video_bytesread + count) > dev->frame_size)
+ count = dev->frame_size - dev->video_bytesread;
+
+ if (copy_to_user(buf, f->bufmem+dev->video_bytesread, count)) {
+ em28xx_err("Error while copying to user\n");
return -EFAULT;
}
+ dev->video_bytesread += count;
+
+ if (dev->video_bytesread == dev->frame_size) {
+ spin_lock_irqsave(&dev->queue_lock, lock_flags);
+ list_for_each_entry(i, &dev->outqueue, frame)
+ i->state = F_UNUSED;
+ INIT_LIST_HEAD(&dev->outqueue);
+ spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
+
+ em28xx_queue_unusedframes(dev);
+ dev->video_bytesread = 0;
+ }
+
*f_pos += count;
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return count;
}
@@ -517,11 +1561,14 @@ em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
{
unsigned int mask = 0;
- struct em28xx *dev = filp->private_data;
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
- if (mutex_lock_interruptible(&dev->fileop_lock))
+ if (unlikely(res_get(fh) < 0))
return POLLERR;
+ mutex_lock(&dev->lock);
+
if (dev->state & DEV_DISCONNECTED) {
em28xx_videodbg("device not present\n");
} else if (dev->state & DEV_MISCONFIGURED) {
@@ -545,83 +1592,61 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
if (!list_empty(&dev->outqueue))
mask |= POLLIN | POLLRDNORM;
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return mask;
}
}
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return POLLERR;
}
/*
- * em28xx_vm_open()
- */
-static void em28xx_vm_open(struct vm_area_struct *vma)
-{
- struct em28xx_frame_t *f = vma->vm_private_data;
- f->vma_use_count++;
-}
-
-/*
- * em28xx_vm_close()
- */
-static void em28xx_vm_close(struct vm_area_struct *vma)
-{
- /* NOTE: buffers are not freed here */
- struct em28xx_frame_t *f = vma->vm_private_data;
-
- if (f->vma_use_count)
- f->vma_use_count--;
-}
-
-static struct vm_operations_struct em28xx_vm_ops = {
- .open = em28xx_vm_open,
- .close = em28xx_vm_close,
-};
-
-/*
* em28xx_v4l2_mmap()
*/
static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
{
- unsigned long size = vma->vm_end - vma->vm_start,
- start = vma->vm_start;
- void *pos;
- u32 i;
-
- struct em28xx *dev = filp->private_data;
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long start = vma->vm_start;
+ void *pos;
+ u32 i;
+
+ if (unlikely(res_get(fh) < 0))
+ return -EBUSY;
- if (mutex_lock_interruptible(&dev->fileop_lock))
- return -ERESTARTSYS;
+ mutex_lock(&dev->lock);
if (dev->state & DEV_DISCONNECTED) {
em28xx_videodbg("mmap: device not present\n");
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return -ENODEV;
}
if (dev->state & DEV_MISCONFIGURED) {
em28xx_videodbg ("mmap: Device is misconfigured; close and "
"open it again\n");
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return -EIO;
}
- if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
- size != PAGE_ALIGN(dev->frame[0].buf.length)) {
- mutex_unlock(&dev->fileop_lock);
+ if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE)) {
+ mutex_unlock(&dev->lock);
return -EINVAL;
}
+ if (size > PAGE_ALIGN(dev->frame[0].buf.length))
+ size = PAGE_ALIGN(dev->frame[0].buf.length);
+
for (i = 0; i < dev->num_frames; i++) {
if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
break;
}
if (i == dev->num_frames) {
em28xx_videodbg("mmap: user supplied mapping address is out of range\n");
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return -EINVAL;
}
@@ -633,7 +1658,7 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
while (size > 0) { /* size is page-aligned */
if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
em28xx_videodbg("mmap: vm_insert_page failed\n");
- mutex_unlock(&dev->fileop_lock);
+ mutex_unlock(&dev->lock);
return -EAGAIN;
}
start += PAGE_SIZE;
@@ -645,900 +1670,210 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
vma->vm_private_data = &dev->frame[i];
em28xx_vm_open(vma);
- mutex_unlock(&dev->fileop_lock);
- return 0;
-}
-
-/*
- * em28xx_get_ctrl()
- * return the current saturation, brightness or contrast, mute state
- */
-static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
-{
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = dev->mute;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = dev->volume;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-/*
- * em28xx_set_ctrl()
- * mute or set new saturation, brightness or contrast
- */
-static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
-{
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value != dev->mute) {
- dev->mute = ctrl->value;
- em28xx_audio_usb_mute(dev, ctrl->value);
- return em28xx_audio_analog_set(dev);
- }
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- dev->volume = ctrl->value;
- return em28xx_audio_analog_set(dev);
- default:
- return -EINVAL;
- }
-}
-
-/*
- * em28xx_stream_interrupt()
- * stops streaming
- */
-static int em28xx_stream_interrupt(struct em28xx *dev)
-{
- int ret = 0;
-
- /* stop reading from the device */
-
- dev->stream = STREAM_INTERRUPT;
- ret = wait_event_timeout(dev->wait_stream,
- (dev->stream == STREAM_OFF) ||
- (dev->state & DEV_DISCONNECTED),
- EM28XX_URB_TIMEOUT);
- if (dev->state & DEV_DISCONNECTED)
- return -ENODEV;
- else if (ret) {
- dev->state |= DEV_MISCONFIGURED;
- em28xx_videodbg("device is misconfigured; close and "
- "open /dev/video%d again\n",
- dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
- return ret;
- }
-
- return 0;
-}
-
-static int em28xx_set_norm(struct em28xx *dev, int width, int height)
-{
- unsigned int hscale, vscale;
- unsigned int maxh, maxw;
-
- maxw = norm_maxw(dev);
- maxh = norm_maxh(dev);
-
- /* width must even because of the YUYV format */
- /* height must be even because of interlacing */
- height &= 0xfffe;
- width &= 0xfffe;
-
- if (height < 32)
- height = 32;
- if (height > maxh)
- height = maxh;
- if (width < 48)
- width = 48;
- if (width > maxw)
- width = maxw;
-
- if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000)
- hscale = 0x3fff;
- width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
-
- if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000)
- vscale = 0x3fff;
- height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
-
- /* set new image size */
- dev->width = width;
- dev->height = height;
- dev->frame_size = dev->width * dev->height * 2;
- dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
- dev->bytesperline = dev->width * 2;
- dev->hscale = hscale;
- dev->vscale = vscale;
-
- em28xx_resolution_set(dev);
-
+ mutex_unlock(&dev->lock);
return 0;
}
-static int em28xx_get_fmt(struct em28xx *dev, struct v4l2_format *format)
-{
- em28xx_videodbg("VIDIOC_G_FMT: type=%s\n",
- (format->type ==V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
- "V4L2_BUF_TYPE_VIDEO_CAPTURE" :
- (format->type ==V4L2_BUF_TYPE_VBI_CAPTURE) ?
- "V4L2_BUF_TYPE_VBI_CAPTURE" :
- (format->type ==V4L2_CAP_SLICED_VBI_CAPTURE) ?
- "V4L2_BUF_TYPE_SLICED_VBI_CAPTURE " :
- "not supported");
-
- switch (format->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- {
- format->fmt.pix.width = dev->width;
- format->fmt.pix.height = dev->height;
- format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- format->fmt.pix.bytesperline = dev->bytesperline;
- format->fmt.pix.sizeimage = dev->frame_size;
- format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- format->fmt.pix.field = dev->interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
+static const struct file_operations em28xx_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = em28xx_v4l2_open,
+ .release = em28xx_v4l2_close,
+ .read = em28xx_v4l2_read,
+ .poll = em28xx_v4l2_poll,
+ .mmap = em28xx_v4l2_mmap,
+ .ioctl = video_ioctl2,
+ .llseek = no_llseek,
+ .compat_ioctl = v4l_compat_ioctl32,
+};
- em28xx_videodbg("VIDIOC_G_FMT: %dx%d\n", dev->width,
- dev->height);
- break;
- }
+static const struct file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = em28xx_v4l2_open,
+ .release = em28xx_v4l2_close,
+ .ioctl = video_ioctl2,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- {
- format->fmt.sliced.service_set=0;
+static const struct video_device em28xx_video_template = {
+ .fops = &em28xx_v4l_fops,
+ .release = video_device_release,
+
+ .minor = -1,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap,
+ .vidioc_g_fmt_cap = vidioc_g_fmt_cap,
+ .vidioc_try_fmt_cap = vidioc_try_fmt_cap,
+ .vidioc_s_fmt_cap = vidioc_s_fmt_cap,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_cropcap = vidioc_cropcap,
+
+ .vidioc_g_fmt_vbi_capture = vidioc_g_fmt_vbi_capture,
+ .vidioc_try_fmt_vbi_capture = vidioc_try_set_vbi_capture,
+ .vidioc_s_fmt_vbi_capture = vidioc_try_set_vbi_capture,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+
+ .tvnorms = V4L2_STD_ALL,
+ .current_norm = V4L2_STD_PAL,
+};
- em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format);
+static struct video_device em28xx_radio_template = {
+ .name = "em28xx-radio",
+ .type = VID_TYPE_TUNER,
+ .fops = &radio_fops,
+ .minor = -1,
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+};
- if (format->fmt.sliced.service_set==0)
- return -EINVAL;
+/******************************** usb interface *****************************************/
- break;
- }
- default:
- return -EINVAL;
- }
- return (0);
-}
+static LIST_HEAD(em28xx_extension_devlist);
+static DEFINE_MUTEX(em28xx_extension_devlist_lock);
-static int em28xx_set_fmt(struct em28xx *dev, unsigned int cmd, struct v4l2_format *format)
+int em28xx_register_extension(struct em28xx_ops *ops)
{
- u32 i;
- int ret = 0;
- int width = format->fmt.pix.width;
- int height = format->fmt.pix.height;
- unsigned int hscale, vscale;
- unsigned int maxh, maxw;
+ struct em28xx *h, *dev = NULL;
- maxw = norm_maxw(dev);
- maxh = norm_maxh(dev);
-
- em28xx_videodbg("%s: type=%s\n",
- cmd == VIDIOC_TRY_FMT ?
- "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT",
- format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
- "V4L2_BUF_TYPE_VIDEO_CAPTURE" :
- format->type == V4L2_BUF_TYPE_VBI_CAPTURE ?
- "V4L2_BUF_TYPE_VBI_CAPTURE " :
- "not supported");
-
- if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
- em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format);
-
- if (format->fmt.sliced.service_set==0)
- return -EINVAL;
+ list_for_each_entry(h, &em28xx_devlist, devlist)
+ dev = h;
- return 0;
- }
+ mutex_lock(&em28xx_extension_devlist_lock);
+ list_add_tail(&ops->next, &em28xx_extension_devlist);
+ if (dev)
+ ops->init(dev);
-
- if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- em28xx_videodbg("%s: requested %dx%d\n",
- cmd == VIDIOC_TRY_FMT ?
- "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT",
- format->fmt.pix.width, format->fmt.pix.height);
-
- /* FIXME: Move some code away from here */
- /* width must even because of the YUYV format */
- /* height must be even because of interlacing */
- height &= 0xfffe;
- width &= 0xfffe;
-
- if (height < 32)
- height = 32;
- if (height > maxh)
- height = maxh;
- if (width < 48)
- width = 48;
- if (width > maxw)
- width = maxw;
-
- if(dev->is_em2800){
- /* the em2800 can only scale down to 50% */
- if(height % (maxh / 2))
- height=maxh;
- if(width % (maxw / 2))
- width=maxw;
- /* according to empiatech support */
- /* the MaxPacketSize is to small to support */
- /* framesizes larger than 640x480 @ 30 fps */
- /* or 640x576 @ 25 fps. As this would cut */
- /* of a part of the image we prefer */
- /* 360x576 or 360x480 for now */
- if(width == maxw && height == maxh)
- width /= 2;
- }
-
- if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000)
- hscale = 0x3fff;
-
- width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
-
- if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000)
- vscale = 0x3fff;
-
- height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
-
- format->fmt.pix.width = width;
- format->fmt.pix.height = height;
- format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- format->fmt.pix.bytesperline = width * 2;
- format->fmt.pix.sizeimage = width * 2 * height;
- format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- format->fmt.pix.field = V4L2_FIELD_INTERLACED;
-
- em28xx_videodbg("%s: returned %dx%d (%d, %d)\n",
- cmd == VIDIOC_TRY_FMT ?
- "VIDIOC_TRY_FMT" :"VIDIOC_S_FMT",
- format->fmt.pix.width, format->fmt.pix.height, hscale, vscale);
-
- if (cmd == VIDIOC_TRY_FMT)
- return 0;
-
- for (i = 0; i < dev->num_frames; i++)
- if (dev->frame[i].vma_use_count) {
- em28xx_videodbg("VIDIOC_S_FMT failed. "
- "Unmap the buffers first.\n");
- return -EINVAL;
- }
-
- /* stop io in case it is already in progress */
- if (dev->stream == STREAM_ON) {
- em28xx_videodbg("VIDIOC_SET_FMT: interrupting stream\n");
- if ((ret = em28xx_stream_interrupt(dev)))
- return ret;
- }
-
- em28xx_release_buffers(dev);
- dev->io = IO_NONE;
-
- /* set new image size */
- dev->width = width;
- dev->height = height;
- dev->frame_size = dev->width * dev->height * 2;
- dev->field_size = dev->frame_size >> 1;
- dev->bytesperline = dev->width * 2;
- dev->hscale = hscale;
- dev->vscale = vscale;
- em28xx_uninit_isoc(dev);
- em28xx_set_alternate(dev);
- em28xx_capture_start(dev, 1);
- em28xx_resolution_set(dev);
- em28xx_init_isoc(dev);
+ printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
+ mutex_unlock(&em28xx_extension_devlist_lock);
return 0;
}
+EXPORT_SYMBOL(em28xx_register_extension);
-/*
- * em28xx_v4l2_do_ioctl()
- * This function is _not_ called directly, but from
- * em28xx_v4l2_ioctl. Userspace
- * copying is done already, arg is a kernel pointer.
- */
-static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
- struct em28xx *dev, unsigned int cmd, void *arg,
- v4l2_kioctl driver_ioctl)
+void em28xx_unregister_extension(struct em28xx_ops *ops)
{
- int ret;
+ struct em28xx *h, *dev = NULL;
- switch (cmd) {
- /* ---------- tv norms ---------- */
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *e = arg;
- unsigned int i;
+ list_for_each_entry(h, &em28xx_devlist, devlist)
+ dev = h;
- i = e->index;
- if (i >= TVNORMS)
- return -EINVAL;
- ret = v4l2_video_std_construct(e, tvnorms[e->index].id,
- tvnorms[e->index].name);
- e->index = i;
- if (ret < 0)
- return ret;
- return 0;
- }
- case VIDIOC_G_STD:
- {
- v4l2_std_id *id = arg;
+ if (dev)
+ ops->fini(dev);
- *id = dev->tvnorm->id;
- return 0;
- }
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg;
- unsigned int i;
-
- for (i = 0; i < TVNORMS; i++)
- if (*id == tvnorms[i].id)
- break;
- if (i == TVNORMS)
- for (i = 0; i < TVNORMS; i++)
- if (*id & tvnorms[i].id)
- break;
- if (i == TVNORMS)
- return -EINVAL;
-
- mutex_lock(&dev->lock);
- dev->tvnorm = &tvnorms[i];
-
- em28xx_set_norm(dev, dev->width, dev->height);
-
- em28xx_i2c_call_clients(dev, VIDIOC_S_STD,
- &dev->tvnorm->id);
-
- mutex_unlock(&dev->lock);
-
- return 0;
- }
-
- /* ------ input switching ---------- */
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *i = arg;
- unsigned int n;
- static const char *iname[] = {
- [EM28XX_VMUX_COMPOSITE1] = "Composite1",
- [EM28XX_VMUX_COMPOSITE2] = "Composite2",
- [EM28XX_VMUX_COMPOSITE3] = "Composite3",
- [EM28XX_VMUX_COMPOSITE4] = "Composite4",
- [EM28XX_VMUX_SVIDEO] = "S-Video",
- [EM28XX_VMUX_TELEVISION] = "Television",
- [EM28XX_VMUX_CABLE] = "Cable TV",
- [EM28XX_VMUX_DVB] = "DVB",
- [EM28XX_VMUX_DEBUG] = "for debug only",
- };
-
- n = i->index;
- if (n >= MAX_EM28XX_INPUT)
- return -EINVAL;
- if (0 == INPUT(n)->type)
- return -EINVAL;
- memset(i, 0, sizeof(*i));
- i->index = n;
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(i->name, iname[INPUT(n)->type]);
- if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
- (EM28XX_VMUX_CABLE == INPUT(n)->type))
- i->type = V4L2_INPUT_TYPE_TUNER;
- for (n = 0; n < ARRAY_SIZE(tvnorms); n++)
- i->std |= tvnorms[n].id;
- return 0;
- }
- case VIDIOC_G_INPUT:
- {
- int *i = arg;
- *i = dev->ctl_input;
-
- return 0;
- }
- case VIDIOC_S_INPUT:
- {
- int *index = arg;
-
- if (*index >= MAX_EM28XX_INPUT)
- return -EINVAL;
- if (0 == INPUT(*index)->type)
- return -EINVAL;
-
- mutex_lock(&dev->lock);
- video_mux(dev, *index);
- mutex_unlock(&dev->lock);
-
- return 0;
- }
- case VIDIOC_G_AUDIO:
- {
- struct v4l2_audio *a = arg;
- unsigned int index = a->index;
-
- if (a->index > 1)
- return -EINVAL;
- memset(a, 0, sizeof(*a));
- index = dev->ctl_ainput;
-
- if (index == 0) {
- strcpy(a->name, "Television");
- } else {
- strcpy(a->name, "Line In");
- }
- a->capability = V4L2_AUDCAP_STEREO;
- a->index = index;
- return 0;
- }
- case VIDIOC_S_AUDIO:
- {
- struct v4l2_audio *a = arg;
-
- if (a->index != dev->ctl_ainput)
- return -EINVAL;
-
- return 0;
- }
-
- /* --- controls ---------------------------------------------- */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
- int i, id=qc->id;
-
- memset(qc,0,sizeof(*qc));
- qc->id=id;
-
- if (!dev->has_msp34xx) {
- for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
- if (qc->id && qc->id == em28xx_qctrl[i].id) {
- memcpy(qc, &(em28xx_qctrl[i]),
- sizeof(*qc));
- return 0;
- }
- }
- }
- em28xx_i2c_call_clients(dev,cmd,qc);
- if (qc->type)
- return 0;
- else
- return -EINVAL;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- int retval=-EINVAL;
-
- if (!dev->has_msp34xx)
- retval=em28xx_get_ctrl(dev, ctrl);
- if (retval==-EINVAL) {
- em28xx_i2c_call_clients(dev,cmd,arg);
- return 0;
- } else return retval;
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- u8 i;
-
- if (!dev->has_msp34xx){
- for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
- if (ctrl->id == em28xx_qctrl[i].id) {
- if (ctrl->value <
- em28xx_qctrl[i].minimum
- || ctrl->value >
- em28xx_qctrl[i].maximum)
- return -ERANGE;
- return em28xx_set_ctrl(dev, ctrl);
- }
- }
- }
-
- em28xx_i2c_call_clients(dev,cmd,arg);
- return 0;
- }
- /* --- tuner ioctls ------------------------------------------ */
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *t = arg;
-
- if (0 != t->index)
- return -EINVAL;
-
- memset(t, 0, sizeof(*t));
- strcpy(t->name, "Tuner");
- mutex_lock(&dev->lock);
- /* let clients fill in the remainder of this struct */
- em28xx_i2c_call_clients(dev, cmd, t);
- mutex_unlock(&dev->lock);
- em28xx_videodbg("VIDIO_G_TUNER: signal=%x, afc=%x\n", t->signal,
- t->afc);
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *t = arg;
-
- if (0 != t->index)
- return -EINVAL;
- mutex_lock(&dev->lock);
- /* let clients handle this */
- em28xx_i2c_call_clients(dev, cmd, t);
- mutex_unlock(&dev->lock);
- return 0;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
-
- memset(f, 0, sizeof(*f));
- f->type = V4L2_TUNER_ANALOG_TV;
- f->frequency = dev->ctl_freq;
-
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
-
- if (0 != f->tuner)
- return -EINVAL;
-
- if (V4L2_TUNER_ANALOG_TV != f->type)
- return -EINVAL;
-
- mutex_lock(&dev->lock);
- dev->ctl_freq = f->frequency;
- em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
- mutex_unlock(&dev->lock);
- return 0;
- }
- case VIDIOC_CROPCAP:
- {
- struct v4l2_cropcap *cc = arg;
-
- if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- cc->bounds.left = 0;
- cc->bounds.top = 0;
- cc->bounds.width = dev->width;
- cc->bounds.height = dev->height;
- cc->defrect = cc->bounds;
- cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
- cc->pixelaspect.denominator = 59;
- return 0;
- }
- case VIDIOC_STREAMON:
- {
- int *type = arg;
-
- if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE
- || dev->io != IO_MMAP)
- return -EINVAL;
-
- if (list_empty(&dev->inqueue))
- return -EINVAL;
-
- dev->stream = STREAM_ON; /* FIXME: Start video capture here? */
-
- em28xx_videodbg("VIDIOC_STREAMON: starting stream\n");
-
- return 0;
- }
- case VIDIOC_STREAMOFF:
- {
- int *type = arg;
- int ret;
-
- if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE
- || dev->io != IO_MMAP)
- return -EINVAL;
-
- if (dev->stream == STREAM_ON) {
- em28xx_videodbg ("VIDIOC_STREAMOFF: interrupting stream\n");
- if ((ret = em28xx_stream_interrupt(dev)))
- return ret;
- }
- em28xx_empty_framequeues(dev);
-
- return 0;
- }
- default:
- return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
- driver_ioctl);
- }
- return 0;
-}
-
-/*
- * em28xx_v4l2_do_ioctl()
- * This function is _not_ called directly, but from
- * em28xx_v4l2_ioctl. Userspace
- * copying is done already, arg is a kernel pointer.
- */
-static int em28xx_video_do_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, void *arg)
-{
- struct em28xx *dev = filp->private_data;
-
- if (!dev)
- return -ENODEV;
-
- if (video_debug > 1)
- v4l_print_ioctl(dev->name,cmd);
-
- switch (cmd) {
-
- /* --- capabilities ------------------------------------------ */
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
-
- memset(cap, 0, sizeof(*cap));
- strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
- strlcpy(cap->card, em28xx_boards[dev->model].name,
- sizeof(cap->card));
- strlcpy(cap->bus_info, dev->udev->dev.bus_id,
- sizeof(cap->bus_info));
- cap->version = EM28XX_VERSION_CODE;
- cap->capabilities =
- V4L2_CAP_SLICED_VBI_CAPTURE |
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_AUDIO |
- V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
- if (dev->has_tuner)
- cap->capabilities |= V4L2_CAP_TUNER;
- return 0;
- }
- /* --- capture ioctls ---------------------------------------- */
- case VIDIOC_ENUM_FMT:
- {
- struct v4l2_fmtdesc *fmtd = arg;
-
- if (fmtd->index != 0)
- return -EINVAL;
- memset(fmtd, 0, sizeof(*fmtd));
- fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strcpy(fmtd->description, "Packed YUY2");
- fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
- memset(fmtd->reserved, 0, sizeof(fmtd->reserved));
- return 0;
- }
- case VIDIOC_G_FMT:
- return em28xx_get_fmt(dev, (struct v4l2_format *) arg);
-
- case VIDIOC_TRY_FMT:
- case VIDIOC_S_FMT:
- return em28xx_set_fmt(dev, cmd, (struct v4l2_format *)arg);
-
- case VIDIOC_REQBUFS:
- {
- struct v4l2_requestbuffers *rb = arg;
- u32 i;
- int ret;
-
- if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- rb->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
-
- if (dev->io == IO_READ) {
- em28xx_videodbg ("method is set to read;"
- " close and open the device again to"
- " choose the mmap I/O method\n");
- return -EINVAL;
- }
-
- for (i = 0; i < dev->num_frames; i++)
- if (dev->frame[i].vma_use_count) {
- em28xx_videodbg ("VIDIOC_REQBUFS failed; previous buffers are still mapped\n");
- return -EINVAL;
- }
-
- if (dev->stream == STREAM_ON) {
- em28xx_videodbg("VIDIOC_REQBUFS: interrupting stream\n");
- if ((ret = em28xx_stream_interrupt(dev)))
- return ret;
- }
-
- em28xx_empty_framequeues(dev);
-
- em28xx_release_buffers(dev);
- if (rb->count)
- rb->count =
- em28xx_request_buffers(dev, rb->count);
-
- dev->frame_current = NULL;
-
- em28xx_videodbg ("VIDIOC_REQBUFS: setting io method to mmap: num bufs %i\n",
- rb->count);
- dev->io = rb->count ? IO_MMAP : IO_NONE;
- return 0;
- }
- case VIDIOC_QUERYBUF:
- {
- struct v4l2_buffer *b = arg;
-
- if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- b->index >= dev->num_frames || dev->io != IO_MMAP)
- return -EINVAL;
-
- memcpy(b, &dev->frame[b->index].buf, sizeof(*b));
-
- if (dev->frame[b->index].vma_use_count) {
- b->flags |= V4L2_BUF_FLAG_MAPPED;
- }
- if (dev->frame[b->index].state == F_DONE)
- b->flags |= V4L2_BUF_FLAG_DONE;
- else if (dev->frame[b->index].state != F_UNUSED)
- b->flags |= V4L2_BUF_FLAG_QUEUED;
- return 0;
- }
- case VIDIOC_QBUF:
- {
- struct v4l2_buffer *b = arg;
- unsigned long lock_flags;
-
- if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- b->index >= dev->num_frames || dev->io != IO_MMAP) {
- return -EINVAL;
- }
-
- if (dev->frame[b->index].state != F_UNUSED) {
- return -EAGAIN;
- }
- dev->frame[b->index].state = F_QUEUED;
-
- /* add frame to fifo */
- spin_lock_irqsave(&dev->queue_lock, lock_flags);
- list_add_tail(&dev->frame[b->index].frame,
- &dev->inqueue);
- spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
-
- return 0;
- }
- case VIDIOC_DQBUF:
- {
- struct v4l2_buffer *b = arg;
- struct em28xx_frame_t *f;
- unsigned long lock_flags;
- int ret = 0;
-
- if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
- || dev->io != IO_MMAP)
- return -EINVAL;
-
- if (list_empty(&dev->outqueue)) {
- if (dev->stream == STREAM_OFF)
- return -EINVAL;
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- ret = wait_event_interruptible
- (dev->wait_frame,
- (!list_empty(&dev->outqueue)) ||
- (dev->state & DEV_DISCONNECTED));
- if (ret)
- return ret;
- if (dev->state & DEV_DISCONNECTED)
- return -ENODEV;
- }
-
- spin_lock_irqsave(&dev->queue_lock, lock_flags);
- f = list_entry(dev->outqueue.next,
- struct em28xx_frame_t, frame);
- list_del(dev->outqueue.next);
- spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
-
- f->state = F_UNUSED;
- memcpy(b, &f->buf, sizeof(*b));
-
- if (f->vma_use_count)
- b->flags |= V4L2_BUF_FLAG_MAPPED;
-
- return 0;
- }
- default:
- return em28xx_do_ioctl(inode, filp, dev, cmd, arg,
- em28xx_video_do_ioctl);
- }
- return 0;
+ mutex_lock(&em28xx_extension_devlist_lock);
+ printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
+ list_del(&ops->next);
+ mutex_unlock(&em28xx_extension_devlist_lock);
}
+EXPORT_SYMBOL(em28xx_unregister_extension);
-/*
- * em28xx_v4l2_ioctl()
- * handle v4l2 ioctl the main action happens in em28xx_v4l2_do_ioctl()
- */
-static int em28xx_v4l2_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
+struct video_device *em28xx_vdev_init(struct em28xx *dev,
+ const struct video_device *template,
+ const int type,
+ const char *type_name)
{
- int ret = 0;
- struct em28xx *dev = filp->private_data;
-
- if (mutex_lock_interruptible(&dev->fileop_lock))
- return -ERESTARTSYS;
-
- if (dev->state & DEV_DISCONNECTED) {
- em28xx_errdev("v4l2 ioctl: device not present\n");
- mutex_unlock(&dev->fileop_lock);
- return -ENODEV;
- }
-
- if (dev->state & DEV_MISCONFIGURED) {
- em28xx_errdev
- ("v4l2 ioctl: device is misconfigured; close and open it again\n");
- mutex_unlock(&dev->fileop_lock);
- return -EIO;
- }
+ struct video_device *vfd;
- ret = video_usercopy(inode, filp, cmd, arg, em28xx_video_do_ioctl);
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->minor = -1;
+ vfd->dev = &dev->udev->dev;
+ vfd->release = video_device_release;
+ vfd->type = type;
- mutex_unlock(&dev->fileop_lock);
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s",
+ dev->name, type_name);
- return ret;
+ return vfd;
}
-static const struct file_operations em28xx_v4l_fops = {
- .owner = THIS_MODULE,
- .open = em28xx_v4l2_open,
- .release = em28xx_v4l2_close,
- .ioctl = em28xx_v4l2_ioctl,
- .read = em28xx_v4l2_read,
- .poll = em28xx_v4l2_poll,
- .mmap = em28xx_v4l2_mmap,
- .llseek = no_llseek,
- .compat_ioctl = v4l_compat_ioctl32,
-
-};
-
-/******************************** usb interface *****************************************/
/*
* em28xx_init_dev()
* allocates and inits the device structs, registers i2c bus and v4l device
*/
static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
- int minor, int model)
+ int minor)
{
+ struct em28xx_ops *ops = NULL;
struct em28xx *dev = *devhandle;
int retval = -ENOMEM;
- int errCode, i;
+ int errCode;
unsigned int maxh, maxw;
dev->udev = udev;
- dev->model = model;
mutex_init(&dev->lock);
+ spin_lock_init(&dev->queue_lock);
init_waitqueue_head(&dev->open);
+ init_waitqueue_head(&dev->wait_frame);
+ init_waitqueue_head(&dev->wait_stream);
dev->em28xx_write_regs = em28xx_write_regs;
dev->em28xx_read_reg = em28xx_read_reg;
dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
dev->em28xx_write_regs_req = em28xx_write_regs_req;
dev->em28xx_read_reg_req = em28xx_read_reg_req;
- dev->is_em2800 = em28xx_boards[model].is_em2800;
- dev->has_tuner = em28xx_boards[model].has_tuner;
- dev->has_msp34xx = em28xx_boards[model].has_msp34xx;
- dev->tda9887_conf = em28xx_boards[model].tda9887_conf;
- dev->decoder = em28xx_boards[model].decoder;
-
- if (tuner >= 0)
- dev->tuner_type = tuner;
- else
- dev->tuner_type = em28xx_boards[model].tuner_type;
+ dev->is_em2800 = em28xx_boards[dev->model].is_em2800;
- dev->video_inputs = em28xx_boards[model].vchannels;
+ errCode = em28xx_read_reg(dev, CHIPID_REG);
+ if (errCode >= 0)
+ em28xx_info("em28xx chip ID = %d\n", errCode);
- for (i = 0; i < TVNORMS; i++)
- if (em28xx_boards[model].norm == tvnorms[i].mode)
- break;
- if (i == TVNORMS)
- i = 0;
+ em28xx_pre_card_setup(dev);
+
+ errCode = em28xx_config(dev);
+ if (errCode) {
+ em28xx_errdev("error configuring device\n");
+ em28xx_devused &= ~(1<<dev->devno);
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ /* register i2c bus */
+ em28xx_i2c_register(dev);
- dev->tvnorm = &tvnorms[i]; /* set default norm */
+ /* Do board specific init and eeprom reading */
+ em28xx_card_setup(dev);
- em28xx_videodbg("tvnorm=%s\n", dev->tvnorm->name);
+ /* Configure audio */
+ em28xx_audio_analog_set(dev);
+
+ /* configure the device */
+ em28xx_config_i2c(dev);
+
+ /* set default norm */
+ dev->norm = em28xx_video_template.current_norm;
maxw = norm_maxw(dev);
maxh = norm_maxh(dev);
@@ -1555,138 +1890,110 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->vscale = 0;
dev->ctl_input = 2;
- /* setup video picture settings for saa7113h */
- memset(&dev->vpic, 0, sizeof(dev->vpic));
- dev->vpic.colour = 128 << 8;
- dev->vpic.hue = 128 << 8;
- dev->vpic.brightness = 128 << 8;
- dev->vpic.contrast = 192 << 8;
- dev->vpic.whiteness = 128 << 8; /* This one isn't used */
- dev->vpic.depth = 16;
- dev->vpic.palette = VIDEO_PALETTE_YUV422;
-
- em28xx_pre_card_setup(dev);
-#ifdef CONFIG_MODULES
- /* request some modules */
- if (dev->decoder == EM28XX_SAA7113 || dev->decoder == EM28XX_SAA7114)
- request_module("saa7115");
- if (dev->decoder == EM28XX_TVP5150)
- request_module("tvp5150");
- if (dev->has_tuner)
- request_module("tuner");
-#endif
errCode = em28xx_config(dev);
- if (errCode) {
- em28xx_errdev("error configuring device\n");
- em28xx_devused&=~(1<<dev->devno);
- kfree(dev);
- return -ENOMEM;
- }
-
- mutex_lock(&dev->lock);
- /* register i2c bus */
- em28xx_i2c_register(dev);
- /* Do board specific init and eeprom reading */
- em28xx_card_setup(dev);
+ list_add_tail(&dev->devlist, &em28xx_devlist);
- /* configure the device */
- em28xx_config_i2c(dev);
-
- mutex_unlock(&dev->lock);
-
- errCode = em28xx_config(dev);
-
-#ifdef CONFIG_MODULES
- if (dev->has_msp34xx)
- request_module("msp3400");
-#endif
- /* allocate and fill v4l2 device struct */
- dev->vdev = video_device_alloc();
+ /* allocate and fill video video_device struct */
+ dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template,
+ VID_TYPE_CAPTURE, "video");
if (NULL == dev->vdev) {
em28xx_errdev("cannot allocate video_device.\n");
- em28xx_devused&=~(1<<dev->devno);
- kfree(dev);
- return -ENOMEM;
- }
-
- dev->vbi_dev = video_device_alloc();
- if (NULL == dev->vbi_dev) {
- em28xx_errdev("cannot allocate video_device.\n");
- kfree(dev->vdev);
- em28xx_devused&=~(1<<dev->devno);
- kfree(dev);
- return -ENOMEM;
+ goto fail_unreg;
}
-
- /* Fills VBI device info */
- dev->vbi_dev->type = VFL_TYPE_VBI;
- dev->vbi_dev->fops = &em28xx_v4l_fops;
- dev->vbi_dev->minor = -1;
- dev->vbi_dev->dev = &dev->udev->dev;
- dev->vbi_dev->release = video_device_release;
- snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name), "%s#%d %s",
- "em28xx",dev->devno,"vbi");
-
- /* Fills CAPTURE device info */
- dev->vdev->type = VID_TYPE_CAPTURE;
- if (dev->has_tuner)
+ if (dev->tuner_type != TUNER_ABSENT)
dev->vdev->type |= VID_TYPE_TUNER;
- dev->vdev->fops = &em28xx_v4l_fops;
- dev->vdev->minor = -1;
- dev->vdev->dev = &dev->udev->dev;
- dev->vdev->release = video_device_release;
- snprintf(dev->vdev->name, sizeof(dev->vbi_dev->name), "%s#%d %s",
- "em28xx",dev->devno,"video");
-
- list_add_tail(&dev->devlist,&em28xx_devlist);
- /* register v4l2 device */
- mutex_lock(&dev->lock);
- if ((retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
- video_nr[dev->devno]))) {
+ /* register v4l2 video video_device */
+ retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
+ video_nr[dev->devno]);
+ if (retval) {
em28xx_errdev("unable to register video device (error=%i).\n",
retval);
- mutex_unlock(&dev->lock);
- list_del(&dev->devlist);
- video_device_release(dev->vdev);
- em28xx_devused&=~(1<<dev->devno);
- kfree(dev);
- return -ENODEV;
+ goto fail_unreg;
}
+ /* Allocate and fill vbi video_device struct */
+ dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
+ VFL_TYPE_VBI, "vbi");
+ /* register v4l2 vbi video_device */
if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
vbi_nr[dev->devno]) < 0) {
- printk("unable to register vbi device\n");
- mutex_unlock(&dev->lock);
- list_del(&dev->devlist);
- video_device_release(dev->vbi_dev);
- video_device_release(dev->vdev);
- em28xx_devused&=~(1<<dev->devno);
- kfree(dev);
- return -ENODEV;
- } else {
- printk("registered VBI\n");
+ em28xx_errdev("unable to register vbi device\n");
+ retval = -ENODEV;
+ goto fail_unreg;
}
+ if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
+ dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template,
+ VFL_TYPE_RADIO, "radio");
+ if (NULL == dev->radio_dev) {
+ em28xx_errdev("cannot allocate video_device.\n");
+ goto fail_unreg;
+ }
+ retval = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+ radio_nr[dev->devno]);
+ if (retval < 0) {
+ em28xx_errdev("can't register radio device\n");
+ goto fail_unreg;
+ }
+ em28xx_info("Registered radio device as /dev/radio%d\n",
+ dev->radio_dev->minor & 0x1f);
+ }
+
+
if (dev->has_msp34xx) {
/* Send a reset to other chips via gpio */
em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1);
msleep(3);
em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1);
msleep(3);
-
}
- video_mux(dev, 0);
- mutex_unlock(&dev->lock);
+ video_mux(dev, 0);
em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
+ mutex_lock(&em28xx_extension_devlist_lock);
+ if (!list_empty(&em28xx_extension_devlist)) {
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->id)
+ ops->init(dev);
+ }
+ }
+ mutex_unlock(&em28xx_extension_devlist_lock);
+
return 0;
+
+fail_unreg:
+ em28xx_release_resources(dev);
+ mutex_unlock(&dev->lock);
+ kfree(dev);
+ return retval;
+}
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct em28xx *dev = container_of(work,
+ struct em28xx, request_module_wk);
+
+ if (dev->has_audio_class)
+ request_module("snd-usb-audio");
+ else
+ request_module("em28xx-alsa");
+}
+
+static void request_modules(struct em28xx *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
}
+#else
+#define request_modules(dev)
+#endif /* CONFIG_MODULES */
/*
* em28xx_usb_probe()
@@ -1700,7 +2007,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
struct usb_interface *uif;
struct em28xx *dev = NULL;
int retval = -ENODEV;
- int model,i,nr,ifnum;
+ int i, nr, ifnum;
udev = usb_get_dev(interface_to_usbdev(interface));
ifnum = interface->altsetting[0].desc.bInterfaceNumber;
@@ -1740,8 +2047,6 @@ static int em28xx_usb_probe(struct usb_interface *interface,
return -ENODEV;
}
- model=id->driver_info;
-
if (nr >= EM28XX_MAXBOARDS) {
printk (DRIVER_NAME ": Supports only %i em28xx boards.\n",EM28XX_MAXBOARDS);
em28xx_devused&=~(1<<nr);
@@ -1757,7 +2062,20 @@ static int em28xx_usb_probe(struct usb_interface *interface,
}
snprintf(dev->name, 29, "em28xx #%d", nr);
- dev->devno=nr;
+ dev->devno = nr;
+ dev->model = id->driver_info;
+
+ /* Checks if audio is provided by some interface */
+ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
+ uif = udev->config->interface[i];
+ if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ dev->has_audio_class = 1;
+ break;
+ }
+ }
+
+ printk(KERN_INFO DRIVER_NAME " %s usb audio class\n",
+ dev->has_audio_class ? "Has" : "Doesn't have");
/* compute alternate max packet sizes */
uif = udev->actconfig->interface[0];
@@ -1784,33 +2102,20 @@ static int em28xx_usb_probe(struct usb_interface *interface,
}
if ((card[nr]>=0)&&(card[nr]<em28xx_bcount))
- model=card[nr];
-
- if ((model==EM2800_BOARD_UNKNOWN)||(model==EM2820_BOARD_UNKNOWN)) {
- em28xx_errdev( "Your board has no eeprom inside it and thus can't\n"
- "%s: be autodetected. Please pass card=<n> insmod option to\n"
- "%s: workaround that. Redirect complaints to the vendor of\n"
- "%s: the TV card. Generic type will be used."
- "%s: Best regards,\n"
- "%s: -- tux\n",
- dev->name,dev->name,dev->name,dev->name,dev->name);
- em28xx_errdev("%s: Here is a list of valid choices for the card=<n> insmod option:\n",
- dev->name);
- for (i = 0; i < em28xx_bcount; i++) {
- em28xx_errdev(" card=%d -> %s\n", i,
- em28xx_boards[i].name);
- }
- }
+ dev->model = card[nr];
/* allocate device struct */
- retval = em28xx_init_dev(&dev, udev, nr, model);
+ retval = em28xx_init_dev(&dev, udev, nr);
if (retval)
return retval;
- em28xx_info("Found %s\n", em28xx_boards[model].name);
+ em28xx_info("Found %s\n", em28xx_boards[dev->model].name);
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
+
+ request_modules(dev);
+
return 0;
}
@@ -1821,18 +2126,20 @@ static int em28xx_usb_probe(struct usb_interface *interface,
*/
static void em28xx_usb_disconnect(struct usb_interface *interface)
{
- struct em28xx *dev = usb_get_intfdata(interface);
+ struct em28xx *dev;
+ struct em28xx_ops *ops = NULL;
+
+ dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
if (!dev)
return;
- down_write(&em28xx_disconnect);
+ em28xx_info("disconnecting %s\n", dev->vdev->name);
+ /* wait until all current v4l2 io is finished then deallocate resources */
mutex_lock(&dev->lock);
- em28xx_info("disconnecting %s\n", dev->vdev->name);
-
wake_up_interruptible_all(&dev->open);
if (dev->users) {
@@ -1850,15 +2157,20 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
dev->state |= DEV_DISCONNECTED;
em28xx_release_resources(dev);
}
-
mutex_unlock(&dev->lock);
+ mutex_lock(&em28xx_extension_devlist_lock);
+ if (!list_empty(&em28xx_extension_devlist)) {
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ ops->fini(dev);
+ }
+ }
+ mutex_unlock(&em28xx_extension_devlist_lock);
+
if (!dev->users) {
kfree(dev->alt_max_pkt_size);
kfree(dev);
}
-
- up_write(&em28xx_disconnect);
}
static struct usb_driver em28xx_usb_driver = {
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index d8fcc9e17ac0..f3bad0c1c517 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -25,28 +25,11 @@
#ifndef _EM28XX_H
#define _EM28XX_H
-#include <linux/videodev.h>
+#include <linux/videodev2.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <media/ir-kbd-i2c.h>
-/* Boards supported by driver */
-
-#define EM2800_BOARD_UNKNOWN 0
-#define EM2820_BOARD_UNKNOWN 1
-#define EM2820_BOARD_TERRATEC_CINERGY_250 2
-#define EM2820_BOARD_PINNACLE_USB_2 3
-#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4
-#define EM2820_BOARD_MSI_VOX_USB_2 5
-#define EM2800_BOARD_TERRATEC_CINERGY_200 6
-#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7
-#define EM2800_BOARD_KWORLD_USB2800 8
-#define EM2820_BOARD_PINNACLE_DVC_90 9
-#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10
-#define EM2880_BOARD_TERRATEC_HYBRID_XS 11
-#define EM2820_BOARD_KWORLD_PVRTV2800RF 12
-#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13
-
#define UNSET -1
/* maximum number of em28xx boards */
@@ -148,10 +131,17 @@ enum enum28xx_itype {
EM28XX_RADIO,
};
+enum em28xx_amux {
+ EM28XX_AMUX_VIDEO,
+ EM28XX_AMUX_LINE_IN,
+ EM28XX_AMUX_AC97_VIDEO,
+ EM28XX_AMUX_AC97_LINE_IN,
+};
+
struct em28xx_input {
enum enum28xx_itype type;
unsigned int vmux;
- unsigned int amux;
+ enum em28xx_amux amux;
};
#define INPUT(nr) (&em28xx_boards[dev->model].input[nr])
@@ -165,19 +155,23 @@ enum em28xx_decoder {
struct em28xx_board {
char *name;
int vchannels;
- int norm;
int tuner_type;
/* i2c flags */
- unsigned int is_em2800;
unsigned int tda9887_conf;
- unsigned int has_tuner:1;
+ unsigned int is_em2800:1;
unsigned int has_msp34xx:1;
+ unsigned int mts_firmware:1;
+ unsigned int has_12mhz_i2s:1;
+ unsigned int max_range_640_480:1;
+
+ unsigned int analog_gpio;
enum em28xx_decoder decoder;
struct em28xx_input input[MAX_EM28XX_INPUT];
+ struct em28xx_input radio;
};
struct em28xx_eeprom {
@@ -201,12 +195,26 @@ enum em28xx_dev_state {
DEV_MISCONFIGURED = 0x04,
};
-/* tvnorms */
-struct em28xx_tvnorm {
- char *name;
- v4l2_std_id id;
- /* mode for saa7113h */
- int mode;
+#define EM28XX_AUDIO_BUFS 5
+#define EM28XX_NUM_AUDIO_PACKETS 64
+#define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */
+#define EM28XX_CAPTURE_STREAM_EN 1
+#define EM28XX_AUDIO 0x10
+
+struct em28xx_audio {
+ char name[50];
+ char *transfer_buffer[EM28XX_AUDIO_BUFS];
+ struct urb *urb[EM28XX_AUDIO_BUFS];
+ struct usb_device *udev;
+ unsigned int capture_transfer_done;
+ struct snd_pcm_substream *capture_pcm_substream;
+
+ unsigned int hwptr_done_capture;
+ struct snd_card *sndcard;
+
+ int users, shutdown;
+ enum em28xx_stream_state capture_stream;
+ spinlock_t slock;
};
/* main device struct */
@@ -215,12 +223,17 @@ struct em28xx {
char name[30]; /* name (including minor) of the device */
int model; /* index in the device_data struct */
int devno; /* marks the number of this device */
- unsigned int is_em2800;
- int video_inputs; /* number of video inputs */
- struct list_head devlist;
- unsigned int has_tuner:1;
+ unsigned int analog_gpio;
+ unsigned int is_em2800:1;
unsigned int has_msp34xx:1;
unsigned int has_tda9887:1;
+ unsigned int stream_on:1; /* Locks streams */
+ unsigned int has_audio_class:1;
+ unsigned int has_12mhz_i2s:1;
+ unsigned int max_range_640_480:1;
+
+ int video_inputs; /* number of video inputs */
+ struct list_head devlist;
u32 i2s_speed; /* I2S speed for audio digital stream */
@@ -235,8 +248,7 @@ struct em28xx {
/* video for linux */
int users; /* user count for exclusive use */
struct video_device *vdev; /* video for linux device struct */
- struct video_picture vpic; /* picture settings only used to init saa7113h */
- struct em28xx_tvnorm *tvnorm; /* selected tv norm */
+ v4l2_std_id norm; /* selected tv norm */
int ctl_freq; /* selected frequency */
unsigned int ctl_input; /* selected input */
unsigned int ctl_ainput; /* slected audio input */
@@ -256,17 +268,27 @@ struct em28xx {
int vscale; /* vertical scale factor (see datasheet) */
int interlaced; /* 1=interlace fileds, 0=just top fileds */
int type;
+ unsigned int video_bytesread; /* Number of bytes read */
+
+ unsigned long hash; /* eeprom hash - for boards with generic ID */
+ unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */
+
+ struct em28xx_audio *adev;
/* states */
enum em28xx_dev_state state;
enum em28xx_stream_state stream;
enum em28xx_io_method io;
+
+ struct work_struct request_module_wk;
+
/* locks */
- struct mutex lock, fileop_lock;
+ struct mutex lock;
spinlock_t queue_lock;
struct list_head inqueue, outqueue;
wait_queue_head_t open, wait_frame, wait_stream;
struct video_device *vbi_dev;
+ struct video_device *radio_dev;
unsigned char eedata[256];
@@ -289,16 +311,27 @@ struct em28xx {
int (*em28xx_read_reg_req) (struct em28xx * dev, u8 req, u16 reg);
};
+struct em28xx_fh {
+ struct em28xx *dev;
+ unsigned int stream_on:1; /* Locks streams */
+ int radio;
+};
+
+struct em28xx_ops {
+ struct list_head next;
+ char *name;
+ int id;
+ int (*init)(struct em28xx *);
+ int (*fini)(struct em28xx *);
+};
+
/* Provided by em28xx-i2c.c */
void em28xx_i2c_call_clients(struct em28xx *dev, unsigned int cmd, void *arg);
+void em28xx_do_i2c_scan(struct em28xx *dev);
int em28xx_i2c_register(struct em28xx *dev);
int em28xx_i2c_unregister(struct em28xx *dev);
-/* Provided by em28xx-input.c */
-
-void em28xx_set_ir(struct em28xx * dev,struct IR_i2c *ir);
-
/* Provided by em28xx-core.c */
u32 em28xx_request_buffers(struct em28xx *dev, u32 count);
@@ -314,8 +347,9 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len);
int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
u8 bitmask);
-int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 * val);
+int em28xx_set_audio_source(struct em28xx *dev);
int em28xx_audio_analog_set(struct em28xx *dev);
+
int em28xx_colorlevels_set_default(struct em28xx *dev);
int em28xx_capture_start(struct em28xx *dev, int start);
int em28xx_outfmt_set_yuv422(struct em28xx *dev);
@@ -324,6 +358,10 @@ int em28xx_init_isoc(struct em28xx *dev);
void em28xx_uninit_isoc(struct em28xx *dev);
int em28xx_set_alternate(struct em28xx *dev);
+/* Provided by em28xx-video.c */
+int em28xx_register_extension(struct em28xx_ops *dev);
+void em28xx_unregister_extension(struct em28xx_ops *dev);
+
/* Provided by em28xx-cards.c */
extern int em2800_variant_detect(struct usb_device* udev,int model);
extern void em28xx_pre_card_setup(struct em28xx *dev);
@@ -331,8 +369,20 @@ extern void em28xx_card_setup(struct em28xx *dev);
extern struct em28xx_board em28xx_boards[];
extern struct usb_device_id em28xx_id_table[];
extern const unsigned int em28xx_bcount;
+void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir);
+
+/* Provided by em28xx-input.c */
+/* TODO: Check if the standard get_key handlers on ir-common can be used */
+int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
+int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
+int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw);
+
+/* em2800 registers */
+#define EM2800_AUDIOSRC_REG 0x08
/* em28xx registers */
+#define I2C_CLK_REG 0x06
#define CHIPID_REG 0x0a
#define USBSUSP_REG 0x0c /* */
@@ -384,9 +434,12 @@ extern const unsigned int em28xx_bcount;
/* em202 registers */
#define MASTER_AC97 0x02
+#define LINE_IN_AC97 0x10
#define VIDEO_AC97 0x14
/* register settings */
+#define EM2800_AUDIO_SRC_TUNER 0x0d
+#define EM2800_AUDIO_SRC_LINE 0x0c
#define EM28XX_AUDIO_SRC_TUNER 0xc0
#define EM28XX_AUDIO_SRC_LINE 0x80
@@ -406,22 +459,6 @@ extern const unsigned int em28xx_bcount;
printk(KERN_WARNING "%s: "fmt,\
dev->name , ##arg); } while (0)
-inline static int em28xx_audio_source(struct em28xx *dev, int input)
-{
- return em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0);
-}
-
-inline static int em28xx_audio_usb_mute(struct em28xx *dev, int mute)
-{
- return em28xx_write_reg_bits(dev, XCLK_REG, mute ? 0x00 : 0x80, 0x80);
-}
-
-inline static int em28xx_audio_analog_setup(struct em28xx *dev)
-{
- /* unmute video mixer with default volume level */
- return em28xx_write_ac97(dev, VIDEO_AC97, "\x08\x08");
-}
-
inline static int em28xx_compression_disable(struct em28xx *dev)
{
/* side effect of disabling scaler and mixer */
@@ -497,18 +534,17 @@ inline static int em28xx_gamma_set(struct em28xx *dev, s32 val)
/*FIXME: maxw should be dependent of alt mode */
inline static unsigned int norm_maxw(struct em28xx *dev)
{
- switch(dev->model){
- case (EM2820_BOARD_MSI_VOX_USB_2): return(640);
- default: return(720);
- }
+ if (dev->max_range_640_480)
+ return 640;
+ else
+ return 720;
}
inline static unsigned int norm_maxh(struct em28xx *dev)
{
- switch(dev->model){
- case (EM2820_BOARD_MSI_VOX_USB_2): return(480);
- default: return (dev->tvnorm->id & V4L2_STD_625_50) ? 576 : 480;
- }
+ if (dev->max_range_640_480)
+ return 480;
+ else
+ return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
}
-
#endif
diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c
index d19d73b81ede..06b6a3ae06c4 100644
--- a/drivers/media/video/et61x251/et61x251_core.c
+++ b/drivers/media/video/et61x251/et61x251_core.c
@@ -227,7 +227,7 @@ int et61x251_write_reg(struct et61x251_device* cam, u8 value, u16 index)
}
-int et61x251_read_reg(struct et61x251_device* cam, u16 index)
+static int et61x251_read_reg(struct et61x251_device* cam, u16 index)
{
struct usb_device* udev = cam->usbdev;
u8* buff = cam->control_buffer;
@@ -269,73 +269,6 @@ et61x251_i2c_wait(struct et61x251_device* cam,
int
-et61x251_i2c_try_read(struct et61x251_device* cam,
- const struct et61x251_sensor* sensor, u8 address)
-{
- struct usb_device* udev = cam->usbdev;
- u8* data = cam->control_buffer;
- int err = 0, res;
-
- data[0] = address;
- data[1] = cam->sensor.i2c_slave_id;
- data[2] = cam->sensor.rsta | 0x10;
- data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02);
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
- 0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT);
- if (res < 0)
- err += res;
-
- err += et61x251_i2c_wait(cam, sensor);
-
- res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
- 0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT);
- if (res < 0)
- err += res;
-
- if (err)
- DBG(3, "I2C read failed for %s image sensor", sensor->name);
-
- PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]);
-
- return err ? -1 : (int)data[0];
-}
-
-
-int
-et61x251_i2c_try_write(struct et61x251_device* cam,
- const struct et61x251_sensor* sensor, u8 address,
- u8 value)
-{
- struct usb_device* udev = cam->usbdev;
- u8* data = cam->control_buffer;
- int err = 0, res;
-
- data[0] = address;
- data[1] = cam->sensor.i2c_slave_id;
- data[2] = cam->sensor.rsta | 0x12;
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
- 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
- if (res < 0)
- err += res;
-
- data[0] = value;
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
- 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT);
- if (res < 0)
- err += res;
-
- err += et61x251_i2c_wait(cam, sensor);
-
- if (err)
- DBG(3, "I2C write failed for %s image sensor", sensor->name);
-
- PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value);
-
- return err ? -1 : 0;
-}
-
-
-int
et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2,
u8 data3, u8 data4, u8 data5, u8 data6, u8 data7,
u8 data8, u8 address)
@@ -387,17 +320,6 @@ et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2,
}
-int et61x251_i2c_read(struct et61x251_device* cam, u8 address)
-{
- return et61x251_i2c_try_read(cam, &cam->sensor, address);
-}
-
-
-int et61x251_i2c_write(struct et61x251_device* cam, u8 address, u8 value)
-{
- return et61x251_i2c_try_write(cam, &cam->sensor, address, value);
-}
-
/*****************************************************************************/
static void et61x251_urb_complete(struct urb *urb)
@@ -675,6 +597,83 @@ static int et61x251_stream_interrupt(struct et61x251_device* cam)
/*****************************************************************************/
#ifdef CONFIG_VIDEO_ADV_DEBUG
+
+static int et61x251_i2c_try_read(struct et61x251_device* cam,
+ const struct et61x251_sensor* sensor,
+ u8 address)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ data[0] = address;
+ data[1] = cam->sensor.i2c_slave_id;
+ data[2] = cam->sensor.rsta | 0x10;
+ data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02);
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += et61x251_i2c_wait(cam, sensor);
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+ 0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ if (err)
+ DBG(3, "I2C read failed for %s image sensor", sensor->name);
+
+ PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]);
+
+ return err ? -1 : (int)data[0];
+}
+
+
+static int et61x251_i2c_try_write(struct et61x251_device* cam,
+ const struct et61x251_sensor* sensor,
+ u8 address, u8 value)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ data[0] = address;
+ data[1] = cam->sensor.i2c_slave_id;
+ data[2] = cam->sensor.rsta | 0x12;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ data[0] = value;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += et61x251_i2c_wait(cam, sensor);
+
+ if (err)
+ DBG(3, "I2C write failed for %s image sensor", sensor->name);
+
+ PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value);
+
+ return err ? -1 : 0;
+}
+
+static int et61x251_i2c_read(struct et61x251_device* cam, u8 address)
+{
+ return et61x251_i2c_try_read(cam, &cam->sensor, address);
+}
+
+static int et61x251_i2c_write(struct et61x251_device* cam,
+ u8 address, u8 value)
+{
+ return et61x251_i2c_try_write(cam, &cam->sensor, address, value);
+}
+
static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count)
{
char str[5];
diff --git a/drivers/media/video/et61x251/et61x251_sensor.h b/drivers/media/video/et61x251/et61x251_sensor.h
index e14586330623..71a03148cb09 100644
--- a/drivers/media/video/et61x251/et61x251_sensor.h
+++ b/drivers/media/video/et61x251/et61x251_sensor.h
@@ -52,14 +52,6 @@ et61x251_attach_sensor(struct et61x251_device* cam,
/*****************************************************************************/
extern int et61x251_write_reg(struct et61x251_device*, u8 value, u16 index);
-extern int et61x251_read_reg(struct et61x251_device*, u16 index);
-extern int et61x251_i2c_write(struct et61x251_device*, u8 address, u8 value);
-extern int et61x251_i2c_read(struct et61x251_device*, u8 address);
-extern int et61x251_i2c_try_write(struct et61x251_device*,
- const struct et61x251_sensor*, u8 address,
- u8 value);
-extern int et61x251_i2c_try_read(struct et61x251_device*,
- const struct et61x251_sensor*, u8 address);
extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1,
u8 data2, u8 data3, u8 data4, u8 data5,
u8 data6, u8 data7, u8 data8, u8 address);
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index 29779d8bf7fb..9851987b95fb 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -398,6 +398,7 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
case 0x7a:
case 0x47:
case 0x71:
+ case 0x2d:
if (adap->id == I2C_HW_B_CX2388x) {
/* Handled by cx88-input */
name = "CX2388x remote";
@@ -504,7 +505,7 @@ static int ir_probe(struct i2c_adapter *adap)
*/
static const int probe_bttv[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1};
- static const int probe_saa7134[] = { 0x7a, 0x47, 0x71, -1 };
+ static const int probe_saa7134[] = { 0x7a, 0x47, 0x71, 0x2d, -1 };
static const int probe_em28XX[] = { 0x30, 0x47, -1 };
static const int probe_cx88[] = { 0x18, 0x6b, 0x71, -1 };
static const int probe_cx23885[] = { 0x6b, -1 };
diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig
index 854cc9c30ca9..270906fc3146 100644
--- a/drivers/media/video/ivtv/Kconfig
+++ b/drivers/media/video/ivtv/Kconfig
@@ -3,6 +3,7 @@ config VIDEO_IVTV
depends on VIDEO_V4L1 && VIDEO_V4L2 && PCI && I2C && EXPERIMENTAL
select I2C_ALGOBIT
select FW_LOADER
+ select VIDEO_IR
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_CX2341X
@@ -12,6 +13,7 @@ config VIDEO_IVTV
select VIDEO_SAA7127
select VIDEO_TVAUDIO
select VIDEO_CS53L32A
+ select VIDEO_M52790
select VIDEO_WM8775
select VIDEO_WM8739
select VIDEO_VP27SMPX
diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile
index e8eefd96d897..a0389014fa88 100644
--- a/drivers/media/video/ivtv/Makefile
+++ b/drivers/media/video/ivtv/Makefile
@@ -6,3 +6,8 @@ ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \
obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o
+
+EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c
index b6a8be622d3c..f23c6b8d6911 100644
--- a/drivers/media/video/ivtv/ivtv-cards.c
+++ b/drivers/media/video/ivtv/ivtv-cards.c
@@ -23,6 +23,7 @@
#include "ivtv-i2c.h"
#include <media/msp3400.h>
+#include <media/m52790.h>
#include <media/wm8775.h>
#include <media/cs53l32a.h>
#include <media/cx25840.h>
@@ -39,6 +40,27 @@
#define MSP_MONO MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+/* usual i2c tuner addresses to probe */
+static struct ivtv_card_tuner_i2c ivtv_i2c_std = {
+ .radio = { I2C_CLIENT_END },
+ .demod = { 0x43, I2C_CLIENT_END },
+ .tv = { 0x61, 0x60, I2C_CLIENT_END },
+};
+
+/* as above, but with possible radio tuner */
+static struct ivtv_card_tuner_i2c ivtv_i2c_radio = {
+ .radio = { 0x60, I2C_CLIENT_END },
+ .demod = { 0x43, I2C_CLIENT_END },
+ .tv = { 0x61, I2C_CLIENT_END },
+};
+
+/* using the tda8290+75a combo */
+static struct ivtv_card_tuner_i2c ivtv_i2c_tda8290 = {
+ .radio = { I2C_CLIENT_END },
+ .demod = { I2C_CLIENT_END },
+ .tv = { 0x4b, I2C_CLIENT_END },
+};
+
/********************** card configuration *******************************/
/* Please add new PCI IDs to: http://pci-ids.ucw.cz/iii
@@ -72,6 +94,7 @@ static const struct ivtv_card ivtv_card_pvr250 = {
{ IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 },
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -126,6 +149,7 @@ static const struct ivtv_card ivtv_card_pvr350 = {
{ IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 },
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+ .i2c = &ivtv_i2c_std,
};
/* PVR-350 V1 boards have a different audio tuner input and use a
@@ -157,6 +181,7 @@ static const struct ivtv_card ivtv_card_pvr350_v1 = {
{ IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 },
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -192,6 +217,7 @@ static const struct ivtv_card ivtv_card_pvr150 = {
CX25840_AUDIO_SERIAL, WM8775_AIN4 },
/* apparently needed for the IR blaster */
.gpio_init = { .direction = 0x1f01, .initial_value = 0x26f3 },
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -234,6 +260,7 @@ static const struct ivtv_card ivtv_card_m179 = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_NTSC },
},
.pci_list = ivtv_pci_m179,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -275,6 +302,7 @@ static const struct ivtv_card ivtv_card_mpg600 = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_mpg600,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -315,6 +343,7 @@ static const struct ivtv_card ivtv_card_mpg160 = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_mpg160,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -350,6 +379,7 @@ static const struct ivtv_card ivtv_card_pg600 = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_pg600,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -393,6 +423,7 @@ static const struct ivtv_card ivtv_card_avc2410 = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
},
.pci_list = ivtv_pci_avc2410,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -463,6 +494,7 @@ static const struct ivtv_card ivtv_card_tg5000tv = {
{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_tg5000tv,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -493,6 +525,7 @@ static const struct ivtv_card ivtv_card_va2000 = {
{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_va2000,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -537,6 +570,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
},
.pci_list = ivtv_pci_cx23416gyc,
+ .i2c = &ivtv_i2c_std,
};
static const struct ivtv_card ivtv_card_cx23416gyc_nogr = {
@@ -567,6 +601,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc_nogr = {
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
},
+ .i2c = &ivtv_i2c_std,
};
static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = {
@@ -596,6 +631,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = {
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
},
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -635,6 +671,7 @@ static const struct ivtv_card ivtv_card_gv_mvprx = {
{ .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 },
},
.pci_list = ivtv_pci_gv_mvprx,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -671,6 +708,7 @@ static const struct ivtv_card ivtv_card_gv_mvprx2e = {
{ .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 },
},
.pci_list = ivtv_pci_gv_mvprx2e,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -705,6 +743,7 @@ static const struct ivtv_card ivtv_card_gotview_pci_dvd = {
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
},
.pci_list = ivtv_pci_gotview_pci_dvd,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -743,6 +782,7 @@ static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = {
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
},
.pci_list = ivtv_pci_gotview_pci_dvd2,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -778,6 +818,7 @@ static const struct ivtv_card ivtv_card_yuan_mpc622 = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_TDA8290 },
},
.pci_list = ivtv_pci_yuan_mpc622,
+ .i2c = &ivtv_i2c_tda8290,
};
/* ------------------------------------------------------------------------- */
@@ -819,6 +860,7 @@ static const struct ivtv_card ivtv_card_dctmvtvp1 = {
{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_dctmvtvp1,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -838,7 +880,7 @@ static const struct ivtv_card ivtv_card_pg600v2 = {
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
.hw_audio_ctrl = IVTV_HW_CX25840,
- .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ .hw_all = IVTV_HW_CX25840,
.video_inputs = {
{ IVTV_CARD_INPUT_SVIDEO1, 0,
CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
@@ -847,10 +889,8 @@ static const struct ivtv_card ivtv_card_pg600v2 = {
.audio_inputs = {
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
},
- .tuners = {
- { .std = V4L2_STD_ALL, .tuner = TUNER_XCEIVE_XC3028 },
- },
.pci_list = ivtv_pci_pg600v2,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -871,17 +911,22 @@ static const struct ivtv_card ivtv_card_club3d = {
.hw_audio_ctrl = IVTV_HW_CX25840,
.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
.video_inputs = {
- { IVTV_CARD_INPUT_SVIDEO1, 0,
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
- { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE3 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 },
},
.audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
},
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */
.tuners = {
- { .std = V4L2_STD_ALL, .tuner = TUNER_XCEIVE_XC3028 },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
},
.pci_list = ivtv_pci_club3d,
+ .i2c = &ivtv_i2c_std,
};
/* ------------------------------------------------------------------------- */
@@ -900,7 +945,7 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = {
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
.hw_audio_ctrl = IVTV_HW_CX25840,
- .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_WM8739,
.video_inputs = {
{ IVTV_CARD_INPUT_SVIDEO1, 0, CX25840_SVIDEO3 },
{ IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 },
@@ -909,10 +954,115 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = {
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
},
.gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, /* enable line-in */
+ .pci_list = ivtv_pci_avertv_mce116,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia PVR-150 Plus (M113) card */
+
+static const struct ivtv_card_pci_info ivtv_pci_aver_pvr150[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc035 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_aver_pvr150 = {
+ .type = IVTV_CARD_AVER_PVR150PLUS,
+ .name = "AVerMedia PVR-150 Plus",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_muxer = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
+ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+ .gpio_init = { .direction = 0x0800, .initial_value = 0 },
+ .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 },
.tuners = {
- { .std = V4L2_STD_ALL, .tuner = TUNER_XCEIVE_XC3028 },
+ /* This card has a Partsnic PTI-5NF05 tuner */
+ { .std = V4L2_STD_525_60, .tuner = TUNER_TCL_2002N },
},
- .pci_list = ivtv_pci_avertv_mce116,
+ .pci_list = ivtv_pci_aver_pvr150,
+ .i2c = &ivtv_i2c_radio,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia EZMaker PCI Deluxe card */
+
+static const struct ivtv_card_pci_info ivtv_pci_aver_ezmaker[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc03f },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_aver_ezmaker = {
+ .type = IVTV_CARD_AVER_EZMAKER,
+ .name = "AVerMedia EZMaker PCI Deluxe",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_WM8739,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_SVIDEO1, 0, CX25840_SVIDEO3 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 0 },
+ },
+ .gpio_init = { .direction = 0x4000, .initial_value = 0x4000 },
+ /* Does not have a tuner */
+ .pci_list = ivtv_pci_aver_ezmaker,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* ASUS Falcon2 */
+
+static const struct ivtv_card_pci_info ivtv_pci_asus_falcon2[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ASUSTEK, 0x4b66 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ASUSTEK, 0x462e },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ASUSTEK, 0x4b2e },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_asus_falcon2 = {
+ .type = IVTV_CARD_ASUS_FALCON2,
+ .name = "ASUS Falcon2",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_muxer = IVTV_HW_M52790,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_M52790 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 2, CX25840_COMPOSITE2 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, M52790_IN_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL,
+ M52790_IN_V2 | M52790_SW1_YCMIX | M52790_SW2_YCMIX },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, M52790_IN_V2 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, M52790_IN_TUNER },
+ .tuners = {
+ { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ },
+ .pci_list = ivtv_pci_asus_falcon2,
+ .i2c = &ivtv_i2c_std,
};
static const struct ivtv_card *ivtv_card_list[] = {
@@ -937,6 +1087,9 @@ static const struct ivtv_card *ivtv_card_list[] = {
&ivtv_card_pg600v2,
&ivtv_card_club3d,
&ivtv_card_avertv_mce116,
+ &ivtv_card_asus_falcon2,
+ &ivtv_card_aver_pvr150,
+ &ivtv_card_aver_ezmaker,
/* Variations of standard cards but with the same PCI IDs.
These cards must come last in this list. */
diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h
index ff46e5ae8653..191aafdd9968 100644
--- a/drivers/media/video/ivtv/ivtv-cards.h
+++ b/drivers/media/video/ivtv/ivtv-cards.h
@@ -45,7 +45,10 @@
#define IVTV_CARD_PG600V2 18 /* Yuan PG600V2/GotView PCI DVD Lite */
#define IVTV_CARD_CLUB3D 19 /* Club3D ZAP-TV1x01 */
#define IVTV_CARD_AVERTV_MCE116 20 /* AVerTV MCE 116 Plus */
-#define IVTV_CARD_LAST 20
+#define IVTV_CARD_ASUS_FALCON2 21 /* ASUS Falcon2 */
+#define IVTV_CARD_AVER_PVR150PLUS 22 /* AVerMedia PVR-150 Plus */
+#define IVTV_CARD_AVER_EZMAKER 23 /* AVerMedia EZMaker PCI Deluxe */
+#define IVTV_CARD_LAST 23
/* Variants of existing cards but with the same PCI IDs. The driver
detects these based on other device information.
@@ -69,6 +72,7 @@
#define IVTV_PCI_ID_HAUPPAUGE_ALT1 0x0270
#define IVTV_PCI_ID_HAUPPAUGE_ALT2 0x4070
#define IVTV_PCI_ID_ADAPTEC 0x9005
+#define IVTV_PCI_ID_ASUSTEK 0x1043
#define IVTV_PCI_ID_AVERMEDIA 0x1461
#define IVTV_PCI_ID_YUAN1 0x12ab
#define IVTV_PCI_ID_YUAN2 0xff01
@@ -80,7 +84,7 @@
#define IVTV_PCI_ID_GOTVIEW1 0xffac
#define IVTV_PCI_ID_GOTVIEW2 0xffad
-/* hardware flags */
+/* hardware flags, no gaps allowed, IVTV_HW_GPIO must always be last */
#define IVTV_HW_CX25840 (1 << 0)
#define IVTV_HW_SAA7115 (1 << 1)
#define IVTV_HW_SAA7127 (1 << 2)
@@ -90,12 +94,12 @@
#define IVTV_HW_CS53L32A (1 << 6)
#define IVTV_HW_TVEEPROM (1 << 7)
#define IVTV_HW_SAA7114 (1 << 8)
-#define IVTV_HW_TVAUDIO (1 << 9)
-#define IVTV_HW_UPD64031A (1 << 10)
-#define IVTV_HW_UPD6408X (1 << 11)
-#define IVTV_HW_SAA717X (1 << 12)
-#define IVTV_HW_WM8739 (1 << 13)
-#define IVTV_HW_VP27SMPX (1 << 14)
+#define IVTV_HW_UPD64031A (1 << 9)
+#define IVTV_HW_UPD6408X (1 << 10)
+#define IVTV_HW_SAA717X (1 << 11)
+#define IVTV_HW_WM8739 (1 << 12)
+#define IVTV_HW_VP27SMPX (1 << 13)
+#define IVTV_HW_M52790 (1 << 14)
#define IVTV_HW_GPIO (1 << 15)
#define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114)
@@ -230,6 +234,12 @@ struct ivtv_card_tuner {
int tuner; /* tuner ID (from tuner.h) */
};
+struct ivtv_card_tuner_i2c {
+ unsigned short radio[2];/* radio tuner i2c address to probe */
+ unsigned short demod[2];/* demodulator i2c address to probe */
+ unsigned short tv[4]; /* tv tuner i2c addresses to probe */
+};
+
/* for card information/parameters */
struct ivtv_card {
int type;
@@ -257,6 +267,7 @@ struct ivtv_card {
struct ivtv_gpio_audio_detect gpio_audio_detect;
struct ivtv_card_tuner tuners[IVTV_CARD_MAX_TUNERS];
+ struct ivtv_card_tuner_i2c *i2c;
/* list of device and subsystem vendor/devices that
correspond to this card type. */
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 6d2dd8764f81..d42f120354e5 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -59,6 +59,7 @@
#include <media/tveeprom.h>
#include <media/saa7115.h>
#include <media/v4l2-chip-ident.h>
+#include "tuner-xc2028.h"
/* var to keep track of the number of array elements in use */
int ivtv_cards_active = 0;
@@ -185,6 +186,9 @@ MODULE_PARM_DESC(cardtype,
"\t\t\t19 = Yuan PG600V2/GotView PCI DVD Lite\n"
"\t\t\t20 = Club3D ZAP-TV1x01\n"
"\t\t\t21 = AverTV MCE 116 Plus\n"
+ "\t\t\t22 = ASUS Falcon2\n"
+ "\t\t\t23 = AverMedia PVR-150 Plus\n"
+ "\t\t\t24 = AverMedia EZMaker PCI Deluxe\n"
"\t\t\t 0 = Autodetect (default)\n"
"\t\t\t-1 = Ignore this card\n\t\t");
MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
@@ -397,6 +401,7 @@ static void ivtv_process_eeprom(struct ivtv *itv)
itv->v4l2_cap = itv->card->v4l2_capabilities;
itv->card_name = itv->card->name;
+ itv->card_i2c = itv->card->i2c;
/* If this is a PVR500 then it should be possible to detect whether it is the
first or second unit by looking at the subsystem device ID: is bit 4 is
@@ -414,7 +419,14 @@ static void ivtv_process_eeprom(struct ivtv *itv)
This detection is needed since the eeprom reports incorrectly that a radio is
present on the second unit. */
if (tv.model / 1000 == 23) {
+ static const struct ivtv_card_tuner_i2c ivtv_i2c_radio = {
+ .radio = { 0x60, I2C_CLIENT_END },
+ .demod = { 0x43, I2C_CLIENT_END },
+ .tv = { 0x61, I2C_CLIENT_END },
+ };
+
itv->card_name = "WinTV PVR 500";
+ itv->card_i2c = &ivtv_i2c_radio;
if (pci_slot == 8 || pci_slot == 9) {
int is_first = (pci_slot & 1) == 0;
@@ -628,10 +640,11 @@ done:
IVTV_ERR("Defaulting to %s card\n", itv->card->name);
IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
IVTV_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
- IVTV_ERR("Prefix your subject line with [UNKNOWN CARD].\n");
+ IVTV_ERR("Prefix your subject line with [UNKNOWN IVTV CARD].\n");
}
itv->v4l2_cap = itv->card->v4l2_capabilities;
itv->card_name = itv->card->name;
+ itv->card_i2c = itv->card->i2c;
}
/* Precondition: the ivtv structure has been memset to 0. Only
@@ -695,6 +708,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv)
atomic_set(&itv->yuv_info.next_dma_frame, -1);
itv->yuv_info.lace_mode = ivtv_yuv_mode;
itv->yuv_info.lace_threshold = ivtv_yuv_threshold;
+ itv->yuv_info.max_frames_buffered = 3;
return 0;
}
@@ -812,75 +826,61 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
return 0;
}
-static void ivtv_request_module(struct ivtv *itv, const char *name)
+static u32 ivtv_request_module(struct ivtv *itv, u32 hw,
+ const char *name, u32 id)
{
+ if ((hw & id) == 0)
+ return hw;
if (request_module(name) != 0) {
IVTV_ERR("Failed to load module %s\n", name);
- } else {
- IVTV_DEBUG_INFO("Loaded module %s\n", name);
+ return hw & ~id;
}
+ IVTV_DEBUG_INFO("Loaded module %s\n", name);
+ return hw;
}
static void ivtv_load_and_init_modules(struct ivtv *itv)
{
u32 hw = itv->card->hw_all;
- int i;
+ unsigned i;
/* load modules */
#ifndef CONFIG_VIDEO_TUNER
- if (hw & IVTV_HW_TUNER) {
- if (itv->options.tuner == TUNER_XCEIVE_XC3028) {
- IVTV_INFO("Xceive tuner not yet supported, only composite and S-Video inputs will be available\n");
- itv->tunerid = 1;
- }
- else {
- ivtv_request_module(itv, "tuner");
- }
- }
+ hw = ivtv_request_module(itv, hw, "tuner", IVTV_HW_TUNER);
#endif
#ifndef CONFIG_VIDEO_CX25840
- if (hw & IVTV_HW_CX25840)
- ivtv_request_module(itv, "cx25840");
+ hw = ivtv_request_module(itv, hw, "cx25840", IVTV_HW_CX25840);
#endif
#ifndef CONFIG_VIDEO_SAA711X
- if (hw & IVTV_HW_SAA711X)
- ivtv_request_module(itv, "saa7115");
+ hw = ivtv_request_module(itv, hw, "saa7115", IVTV_HW_SAA711X);
#endif
#ifndef CONFIG_VIDEO_SAA7127
- if (hw & IVTV_HW_SAA7127)
- ivtv_request_module(itv, "saa7127");
+ hw = ivtv_request_module(itv, hw, "saa7127", IVTV_HW_SAA7127);
#endif
- if (hw & IVTV_HW_SAA717X)
- ivtv_request_module(itv, "saa717x");
+ hw = ivtv_request_module(itv, hw, "saa717x", IVTV_HW_SAA717X);
#ifndef CONFIG_VIDEO_UPD64031A
- if (hw & IVTV_HW_UPD64031A)
- ivtv_request_module(itv, "upd64031a");
+ hw = ivtv_request_module(itv, hw, "upd64031a", IVTV_HW_UPD64031A);
#endif
#ifndef CONFIG_VIDEO_UPD64083
- if (hw & IVTV_HW_UPD6408X)
- ivtv_request_module(itv, "upd64083");
+ hw = ivtv_request_module(itv, hw, "upd64083", IVTV_HW_UPD6408X);
#endif
#ifndef CONFIG_VIDEO_MSP3400
- if (hw & IVTV_HW_MSP34XX)
- ivtv_request_module(itv, "msp3400");
+ hw = ivtv_request_module(itv, hw, "msp3400", IVTV_HW_MSP34XX);
#endif
#ifndef CONFIG_VIDEO_VP27SMPX
- if (hw & IVTV_HW_VP27SMPX)
- ivtv_request_module(itv, "vp27smpx");
+ hw = ivtv_request_module(itv, hw, "vp27smpx", IVTV_HW_VP27SMPX);
#endif
- if (hw & IVTV_HW_TVAUDIO)
- ivtv_request_module(itv, "tvaudio");
#ifndef CONFIG_VIDEO_WM8775
- if (hw & IVTV_HW_WM8775)
- ivtv_request_module(itv, "wm8775");
+ hw = ivtv_request_module(itv, hw, "wm8775", IVTV_HW_WM8775);
#endif
#ifndef CONFIG_VIDEO_WM8739
- if (hw & IVTV_HW_WM8739)
- ivtv_request_module(itv, "wm8739");
+ hw = ivtv_request_module(itv, hw, "wm8739", IVTV_HW_WM8739);
#endif
#ifndef CONFIG_VIDEO_CS53L32A
- if (hw & IVTV_HW_CS53L32A)
- ivtv_request_module(itv, "cs53l32a");
+ hw = ivtv_request_module(itv, hw, "cs53l32a", IVTV_HW_CS53L32A);
+#endif
+#ifndef CONFIG_VIDEO_M52790
+ hw = ivtv_request_module(itv, hw, "m52790", IVTV_HW_M52790);
#endif
/* check which i2c devices are actually found */
@@ -889,11 +889,12 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
if (!(device & hw))
continue;
- if (device == IVTV_HW_GPIO) {
- /* GPIO is always available */
- itv->hw_flags |= IVTV_HW_GPIO;
+ if (device == IVTV_HW_GPIO || device == IVTV_HW_TVEEPROM) {
+ /* GPIO and TVEEPROM do not use i2c probing */
+ itv->hw_flags |= device;
continue;
}
+ ivtv_i2c_register(itv, i);
if (ivtv_i2c_hw_addr(itv, device) > 0)
itv->hw_flags |= device;
}
@@ -964,7 +965,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
const struct pci_device_id *pci_id)
{
int retval = 0;
- int yuv_buf_size;
int vbi_buf_size;
struct ivtv *itv;
@@ -979,7 +979,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
}
itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC);
- if (itv == 0) {
+ if (itv == NULL) {
spin_unlock(&ivtv_cards_lock);
return -ENOMEM;
}
@@ -1068,9 +1068,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active);
if (itv->card->hw_all & IVTV_HW_TVEEPROM) {
-#ifdef CONFIG_VIDEO_TVEEPROM_MODULE
- ivtv_request_module(itv, "tveeprom");
-#endif
/* Based on the model number the cardtype may be changed.
The PCI IDs are not always reliable. */
ivtv_process_eeprom(itv);
@@ -1111,16 +1108,19 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
itv->is_50hz = 1;
itv->is_out_50hz = 1;
}
+
+ itv->yuv_info.osd_full_w = 720;
+ itv->yuv_info.osd_full_h = itv->is_out_50hz ? 576 : 480;
+ itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w;
+ itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h;
+
itv->params.video_gop_size = itv->is_60hz ? 15 : 12;
itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000;
-
- /* 0x15180 == 720 * 480 / 4, 0x19500 == 720 * 576 / 4 */
- yuv_buf_size = itv->is_60hz ? 0x15180 : 0x19500;
- itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = yuv_buf_size / 2;
- itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = yuv_buf_size / 8;
+ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = 0x10000;
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = 0x08000;
/* Setup VBI Raw Size. Should be big enough to hold PAL.
It is possible to switch between PAL and NTSC, so we need to
@@ -1140,13 +1140,26 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
if (itv->options.radio > 0)
itv->v4l2_cap |= V4L2_CAP_RADIO;
- if (itv->options.tuner > -1 && itv->tunerid == 0) {
+ if (itv->options.tuner > -1) {
struct tuner_setup setup;
setup.addr = ADDR_UNSET;
setup.type = itv->options.tuner;
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
+ setup.tuner_callback = (setup.type == TUNER_XC2028) ?
+ ivtv_reset_tuner_gpio : NULL;
ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup);
+ if (setup.type == TUNER_XC2028) {
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ };
+ struct v4l2_priv_tun_config cfg = {
+ .tuner = itv->options.tuner,
+ .priv = &ctrl,
+ };
+ ivtv_call_i2c_clients(itv, TUNER_SET_CONFIG, &cfg);
+ }
}
/* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index 49ce14d14a54..536140f0c19e 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -65,7 +65,6 @@
#include <linux/ivtv.h>
-
/* Memory layout */
#define IVTV_ENCODER_OFFSET 0x00000000
#define IVTV_ENCODER_SIZE 0x00800000 /* Total size is 0x01000000, but only first half is used */
@@ -392,6 +391,9 @@ struct yuv_frame_info
u32 tru_h;
u32 offset_y;
s32 lace_mode;
+ u32 sync_field;
+ u32 delay;
+ u32 interlaced;
};
#define IVTV_YUV_MODE_INTERLACED 0x00
@@ -403,6 +405,8 @@ struct yuv_frame_info
#define IVTV_YUV_SYNC_ODD 0x04
#define IVTV_YUV_SYNC_MASK 0x04
+#define IVTV_YUV_BUFFERS 8
+
struct yuv_playback_info
{
u32 reg_2834;
@@ -461,9 +465,10 @@ struct yuv_playback_info
u32 osd_vis_w;
u32 osd_vis_h;
- int decode_height;
+ u32 osd_full_w;
+ u32 osd_full_h;
- int frame_interlaced;
+ int decode_height;
int lace_mode;
int lace_threshold;
@@ -475,16 +480,23 @@ struct yuv_playback_info
u32 yuv_forced_update;
int update_frame;
- int sync_field[4]; /* Field to sync on */
- int field_delay[4]; /* Flag to extend duration of previous frame */
u8 fields_lapsed; /* Counter used when delaying a frame */
- struct yuv_frame_info new_frame_info[4];
+ struct yuv_frame_info new_frame_info[IVTV_YUV_BUFFERS];
struct yuv_frame_info old_frame_info;
struct yuv_frame_info old_frame_info_args;
void *blanking_ptr;
dma_addr_t blanking_dmaptr;
+
+ int stream_size;
+
+ u8 draw_frame; /* PVR350 buffer to draw into */
+ u8 max_frames_buffered; /* Maximum number of frames to buffer */
+
+ struct v4l2_rect main_rect;
+ u32 v4l2_src_w;
+ u32 v4l2_src_h;
};
#define IVTV_VBI_FRAMES 32
@@ -577,13 +589,13 @@ struct ivtv {
struct pci_dev *dev; /* PCI device */
const struct ivtv_card *card; /* card information */
const char *card_name; /* full name of the card */
+ const struct ivtv_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */
u8 has_cx23415; /* 1 if it is a cx23415 based card, 0 for cx23416 */
u8 pvr150_workaround; /* 1 if the cx25840 needs to workaround a PVR150 bug */
u8 nof_inputs; /* number of video inputs */
u8 nof_audio_inputs; /* number of audio inputs */
u32 v4l2_cap; /* V4L2 capabilities of card */
u32 hw_flags; /* hardware description of the board */
- int tunerid; /* userspace tuner ID for experimental Xceive tuner support */
v4l2_std_id tuner_std; /* the norm of the card's tuner (fixed) */
/* controlling video decoder function */
int (*video_dec_func)(struct ivtv *, unsigned int, void *);
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
index a200a8a95a2d..6fb96f19a866 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -542,6 +542,7 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c
struct ivtv_open_id *id = filp->private_data;
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
+ struct yuv_playback_info *yi = &itv->yuv_info;
struct ivtv_buffer *buf;
struct ivtv_queue q;
int bytes_written = 0;
@@ -580,6 +581,24 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c
set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
retry:
+ /* If possible, just DMA the entire frame - Check the data transfer size
+ 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)) {
+ bytes_written += itv->dma_data_req_size;
+ user_buf += itv->dma_data_req_size;
+ count -= itv->dma_data_req_size;
+ } else {
+ break;
+ }
+ }
+ if (count == 0) {
+ IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
+ return bytes_written;
+ }
+ }
+
for (;;) {
/* Gather buffers */
while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io)))
@@ -604,9 +623,16 @@ retry:
/* copy user data into buffers */
while ((buf = ivtv_dequeue(s, &q))) {
- /* Make sure we really got all the user data */
- rc = ivtv_buf_copy_from_user(s, buf, user_buf, count);
+ /* yuv is a pain. Don't copy more data than needed for a single
+ frame, otherwise we lose sync with the incoming stream */
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV &&
+ yi->stream_size + count > itv->dma_data_req_size)
+ rc = ivtv_buf_copy_from_user(s, buf, user_buf,
+ itv->dma_data_req_size - yi->stream_size);
+ else
+ rc = ivtv_buf_copy_from_user(s, buf, user_buf, count);
+ /* Make sure we really got all the user data */
if (rc < 0) {
ivtv_queue_move(s, &q, NULL, &s->q_free, 0);
return rc;
@@ -615,6 +641,16 @@ retry:
count -= rc;
bytes_written += rc;
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
+ yi->stream_size += rc;
+ /* If we have a complete yuv frame, break loop now */
+ if (yi->stream_size == itv->dma_data_req_size) {
+ ivtv_enqueue(s, buf, &s->q_full);
+ yi->stream_size = 0;
+ break;
+ }
+ }
+
if (buf->bytesused != s->buf_size) {
/* incomplete, leave in q_io for next time */
ivtv_enqueue(s, buf, &s->q_io);
@@ -642,6 +678,9 @@ retry:
if (s->q_full.length >= itv->dma_data_req_size) {
int got_sig;
+ if (mode == OUT_YUV)
+ ivtv_yuv_setup_stream_frame(itv);
+
prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
while (!(got_sig = signal_pending(current)) &&
test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) {
@@ -922,10 +961,15 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
}
/* YUV or MPG Decoding Mode? */
- if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG) {
clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
- else if (s->type == IVTV_DEC_STREAM_TYPE_YUV)
+ } else if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
+ /* For yuv, we need to know the dma size before we start */
+ itv->dma_data_req_size =
+ 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
+ itv->yuv_info.stream_size = 0;
+ }
return 0;
}
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
index 132fb5f71366..688cd3856685 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -22,6 +22,7 @@
#include "ivtv-driver.h"
#include "ivtv-cards.h"
#include "ivtv-gpio.h"
+#include "tuner-xc2028.h"
#include <media/tuner.h>
/*
@@ -122,6 +123,29 @@ void ivtv_reset_ir_gpio(struct ivtv *itv)
write_reg(curdir, IVTV_REG_GPIO_DIR);
}
+/* Xceive tuner reset function */
+int ivtv_reset_tuner_gpio(void *dev, int cmd, int value)
+{
+ struct i2c_algo_bit_data *algo = dev;
+ struct ivtv *itv = algo->data;
+ int curdir, curout;
+
+ if (cmd != XC2028_TUNER_RESET)
+ return 0;
+ IVTV_DEBUG_INFO("Resetting tuner\n");
+ curout = read_reg(IVTV_REG_GPIO_OUT);
+ curdir = read_reg(IVTV_REG_GPIO_DIR);
+ curdir |= (1 << 12); /* GPIO bit 12 */
+
+ curout &= ~(1 << 12);
+ write_reg(curout, IVTV_REG_GPIO_OUT);
+ schedule_timeout_interruptible(msecs_to_jiffies(1));
+
+ curout |= (1 << 12);
+ write_reg(curout, IVTV_REG_GPIO_OUT);
+ schedule_timeout_interruptible(msecs_to_jiffies(1));
+ return 0;
+}
void ivtv_gpio_init(struct ivtv *itv)
{
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
index 36e54f78aa2a..fa5ab1eb1800 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.c
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -80,6 +80,7 @@
#endif /* I2C_ADAP_CLASS_TV_ANALOG */
#define IVTV_CS53L32A_I2C_ADDR 0x11
+#define IVTV_M52790_I2C_ADDR 0x48
#define IVTV_CX25840_I2C_ADDR 0x44
#define IVTV_SAA7115_I2C_ADDR 0x21
#define IVTV_SAA7127_I2C_ADDR 0x44
@@ -91,7 +92,8 @@
#define IVTV_TEA5767_I2C_ADDR 0x60
#define IVTV_UPD64031A_I2C_ADDR 0x12
#define IVTV_UPD64083_I2C_ADDR 0x5c
-#define IVTV_TDA985X_I2C_ADDR 0x5b
+#define IVTV_VP27SMPX_I2C_ADDR 0x5b
+#define IVTV_M52790_I2C_ADDR 0x48
/* This array should match the IVTV_HW_ defines */
static const u8 hw_driverids[] = {
@@ -104,18 +106,38 @@ static const u8 hw_driverids[] = {
I2C_DRIVERID_CS53L32A,
I2C_DRIVERID_TVEEPROM,
I2C_DRIVERID_SAA711X,
- I2C_DRIVERID_TVAUDIO,
I2C_DRIVERID_UPD64031A,
I2C_DRIVERID_UPD64083,
I2C_DRIVERID_SAA717X,
I2C_DRIVERID_WM8739,
I2C_DRIVERID_VP27SMPX,
+ I2C_DRIVERID_M52790,
+ 0 /* IVTV_HW_GPIO dummy driver ID */
+};
+
+/* This array should match the IVTV_HW_ defines */
+static const u8 hw_addrs[] = {
+ IVTV_CX25840_I2C_ADDR,
+ IVTV_SAA7115_I2C_ADDR,
+ IVTV_SAA7127_I2C_ADDR,
+ IVTV_MSP3400_I2C_ADDR,
+ 0,
+ IVTV_WM8775_I2C_ADDR,
+ IVTV_CS53L32A_I2C_ADDR,
+ 0,
+ IVTV_SAA7115_I2C_ADDR,
+ IVTV_UPD64031A_I2C_ADDR,
+ IVTV_UPD64083_I2C_ADDR,
+ IVTV_SAA717x_I2C_ADDR,
+ IVTV_WM8739_I2C_ADDR,
+ IVTV_VP27SMPX_I2C_ADDR,
+ IVTV_M52790_I2C_ADDR,
0 /* IVTV_HW_GPIO dummy driver ID */
};
/* This array should match the IVTV_HW_ defines */
static const char * const hw_drivernames[] = {
- "cx2584x",
+ "cx25840",
"saa7115",
"saa7127",
"msp3400",
@@ -123,31 +145,67 @@ static const char * const hw_drivernames[] = {
"wm8775",
"cs53l32a",
"tveeprom",
- "saa7114",
- "tvaudio",
+ "saa7115",
"upd64031a",
"upd64083",
"saa717x",
"wm8739",
"vp27smpx",
+ "m52790",
"gpio",
};
-static int attach_inform(struct i2c_client *client)
+int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
{
- struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
+ struct i2c_board_info info;
+ struct i2c_client *c;
+ u8 id;
int i;
- IVTV_DEBUG_I2C("i2c client attach\n");
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- if (itv->i2c_clients[i] == NULL) {
- itv->i2c_clients[i] = client;
- break;
- }
- }
+ IVTV_DEBUG_I2C("i2c client register\n");
+ if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
+ return -1;
+ id = hw_driverids[idx];
+ memset(&info, 0, sizeof(info));
+ strcpy(info.driver_name, hw_drivernames[idx]);
+ info.addr = hw_addrs[idx];
+ for (i = 0; itv->i2c_clients[i] && i < I2C_CLIENTS_MAX; i++) {}
+
if (i == I2C_CLIENTS_MAX) {
- IVTV_ERR("Insufficient room for new I2C client\n");
+ IVTV_ERR("insufficient room for new I2C client!\n");
+ return -ENOMEM;
}
+
+ if (id != I2C_DRIVERID_TUNER) {
+ c = i2c_new_device(&itv->i2c_adap, &info);
+ if (c->driver == NULL)
+ i2c_unregister_device(c);
+ else
+ itv->i2c_clients[i] = c;
+ return itv->i2c_clients[i] ? 0 : -ENODEV;
+ }
+
+ /* special tuner handling */
+ c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->radio);
+ if (c && c->driver == NULL)
+ i2c_unregister_device(c);
+ else if (c)
+ itv->i2c_clients[i++] = c;
+ c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->demod);
+ if (c && c->driver == NULL)
+ i2c_unregister_device(c);
+ else if (c)
+ itv->i2c_clients[i++] = c;
+ c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->tv);
+ if (c && c->driver == NULL)
+ i2c_unregister_device(c);
+ else if (c)
+ itv->i2c_clients[i++] = c;
+ return 0;
+}
+
+static int attach_inform(struct i2c_client *client)
+{
return 0;
}
@@ -475,9 +533,6 @@ static struct i2c_adapter ivtv_i2c_adap_hw_template = {
.client_register = attach_inform,
.client_unregister = detach_inform,
.owner = THIS_MODULE,
-#ifdef I2C_ADAP_CLASS_TV_ANALOG
- .class = I2C_ADAP_CLASS_TV_ANALOG,
-#endif
};
static void ivtv_setscl_old(void *data, int state)
@@ -525,15 +580,12 @@ static int ivtv_getsda_old(void *data)
/* template for i2c-bit-algo */
static struct i2c_adapter ivtv_i2c_adap_template = {
.name = "ivtv i2c driver",
- .id = I2C_HW_B_CX2341X, /* algo-bit is OR'd with this */
+ .id = I2C_HW_B_CX2341X,
.algo = NULL, /* set by i2c-algo-bit */
.algo_data = NULL, /* filled from template */
.client_register = attach_inform,
.client_unregister = detach_inform,
.owner = THIS_MODULE,
-#ifdef I2C_ADAP_CLASS_TV_ANALOG
- .class = I2C_ADAP_CLASS_TV_ANALOG,
-#endif
};
static const struct i2c_algo_bit_data ivtv_i2c_algo_template = {
@@ -558,12 +610,9 @@ int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg
IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
client = itv->i2c_clients[i];
- if (client == NULL) {
- continue;
- }
- if (client->driver->command == NULL) {
+ if (client == NULL || client->driver == NULL ||
+ client->driver->command == NULL)
continue;
- }
if (addr == client->addr) {
retval = client->driver->command(client, cmd, arg);
return retval;
@@ -584,7 +633,7 @@ static int ivtv_i2c_id_addr(struct ivtv *itv, u32 id)
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
client = itv->i2c_clients[i];
- if (client == NULL)
+ if (client == NULL || client->driver == NULL)
continue;
if (id == client->driver->id) {
retval = client->addr;
@@ -710,6 +759,16 @@ int init_ivtv_i2c(struct ivtv *itv)
{
IVTV_DEBUG_I2C("i2c init\n");
+ /* Sanity checks for the I2C hardware arrays. They must be the
+ * 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) ||
+ IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1)) ||
+ hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) {
+ IVTV_ERR("Mismatched I2C hardware arrays\n");
+ return -ENODEV;
+ }
if (itv->options.newi2c > 0) {
memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template,
sizeof(struct i2c_adapter));
@@ -718,9 +777,9 @@ int init_ivtv_i2c(struct ivtv *itv)
sizeof(struct i2c_adapter));
memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template,
sizeof(struct i2c_algo_bit_data));
- itv->i2c_algo.data = itv;
- itv->i2c_adap.algo_data = &itv->i2c_algo;
}
+ itv->i2c_algo.data = itv;
+ itv->i2c_adap.algo_data = &itv->i2c_algo;
sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d",
itv->num);
diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h
index 987042c09b64..022978cf533d 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.h
+++ b/drivers/media/video/ivtv/ivtv-i2c.h
@@ -33,6 +33,7 @@ int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg);
int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg);
int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg);
void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_i2c_register(struct ivtv *itv, unsigned idx);
/* init + register i2c algo-bit adapter */
int init_ivtv_i2c(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index fd6826f472e3..edef2a579617 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -372,7 +372,7 @@ static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fm
fmt->fmt.pix.height = itv->main_rect.height;
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
- if (itv->output_mode == OUT_UDMA_YUV) {
+ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) {
case IVTV_YUV_MODE_INTERLACED:
fmt->fmt.pix.field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ?
@@ -386,14 +386,13 @@ static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fm
break;
}
fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
+ fmt->fmt.pix.bytesperline = 720;
+ fmt->fmt.pix.width = itv->yuv_info.v4l2_src_w;
+ fmt->fmt.pix.height = itv->yuv_info.v4l2_src_h;
/* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
fmt->fmt.pix.sizeimage =
- fmt->fmt.pix.height * fmt->fmt.pix.width +
- fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
- }
- else if (itv->output_mode == OUT_YUV ||
- streamtype == IVTV_ENC_STREAM_TYPE_YUV ||
- streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ 1080 * ((fmt->fmt.pix.height + 31) & ~31);
+ } else if (streamtype == IVTV_ENC_STREAM_TYPE_YUV) {
fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
/* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
fmt->fmt.pix.sizeimage =
@@ -490,6 +489,7 @@ static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fm
static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype,
struct v4l2_format *fmt, int set_fmt)
{
+ struct yuv_playback_info *yi = &itv->yuv_info;
struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
u16 set;
@@ -505,39 +505,52 @@ static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype,
r.width = fmt->fmt.pix.width;
r.height = fmt->fmt.pix.height;
ivtv_get_fmt(itv, streamtype, fmt);
- if (itv->output_mode != OUT_UDMA_YUV) {
- /* TODO: would setting the rect also be valid for this mode? */
- fmt->fmt.pix.width = r.width;
- fmt->fmt.pix.height = r.height;
- }
- if (itv->output_mode == OUT_UDMA_YUV) {
- /* TODO: add checks for validity */
+ fmt->fmt.pix.width = r.width;
+ fmt->fmt.pix.height = r.height;
+ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
fmt->fmt.pix.field = field;
+ if (fmt->fmt.pix.width < 2)
+ fmt->fmt.pix.width = 2;
+ if (fmt->fmt.pix.width > 720)
+ fmt->fmt.pix.width = 720;
+ if (fmt->fmt.pix.height < 2)
+ fmt->fmt.pix.height = 2;
+ if (fmt->fmt.pix.height > 576)
+ fmt->fmt.pix.height = 576;
}
- if (set_fmt) {
- if (itv->output_mode == OUT_UDMA_YUV) {
- switch (field) {
- case V4L2_FIELD_NONE:
- itv->yuv_info.lace_mode = IVTV_YUV_MODE_PROGRESSIVE;
- break;
- case V4L2_FIELD_ANY:
- itv->yuv_info.lace_mode = IVTV_YUV_MODE_AUTO;
- break;
- case V4L2_FIELD_INTERLACED_BT:
- itv->yuv_info.lace_mode =
- IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD;
- break;
- case V4L2_FIELD_INTERLACED_TB:
- default:
- itv->yuv_info.lace_mode = IVTV_YUV_MODE_INTERLACED;
- break;
- }
- itv->yuv_info.lace_sync_field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1;
+ if (set_fmt && streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ /* Return now if we already have some frame data */
+ if (yi->stream_size)
+ return -EBUSY;
- /* Force update of yuv registers */
- itv->yuv_info.yuv_forced_update = 1;
- return 0;
+ yi->v4l2_src_w = r.width;
+ yi->v4l2_src_h = r.height;
+
+ switch (field) {
+ case V4L2_FIELD_NONE:
+ yi->lace_mode = IVTV_YUV_MODE_PROGRESSIVE;
+ break;
+ case V4L2_FIELD_ANY:
+ yi->lace_mode = IVTV_YUV_MODE_AUTO;
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ yi->lace_mode =
+ IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD;
+ break;
+ case V4L2_FIELD_INTERLACED_TB:
+ default:
+ yi->lace_mode = IVTV_YUV_MODE_INTERLACED;
+ break;
}
+ yi->lace_sync_field = (yi->lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1;
+
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+ itv->dma_data_req_size =
+ 1080 * ((yi->v4l2_src_h + 31) & ~31);
+
+ /* Force update of yuv registers */
+ yi->yuv_forced_update = 1;
+ return 0;
}
return 0;
}
@@ -660,11 +673,8 @@ static int ivtv_debug_ioctls(struct file *filp, unsigned int cmd, void *arg)
chip->ident = V4L2_IDENT_NONE;
chip->revision = 0;
if (reg->match_type == V4L2_CHIP_MATCH_HOST) {
- if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) {
- struct v4l2_chip_ident *chip = arg;
-
+ if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
- }
return 0;
}
if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
@@ -688,7 +698,7 @@ static int ivtv_debug_ioctls(struct file *filp, unsigned int cmd, void *arg)
ivtv_reset_ir_gpio(itv);
}
if (val & 0x02) {
- itv->video_dec_func(itv, cmd, 0);
+ itv->video_dec_func(itv, cmd, NULL);
}
break;
}
@@ -703,8 +713,12 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
{
struct ivtv_open_id *id = NULL;
u32 data[CX2341X_MBOX_MAX_DATA];
+ int streamtype = 0;
- if (filp) id = (struct ivtv_open_id *)filp->private_data;
+ if (filp) {
+ id = (struct ivtv_open_id *)filp->private_data;
+ streamtype = id->type;
+ }
switch (cmd) {
case VIDIOC_G_PRIORITY:
@@ -822,6 +836,11 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
cropcap->bounds.height = itv->is_50hz ? 576 : 480;
cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10;
cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11;
+ } else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ cropcap->bounds.width = itv->yuv_info.osd_full_w;
+ cropcap->bounds.height = itv->yuv_info.osd_full_h;
+ cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
+ cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
} else {
cropcap->bounds.height = itv->is_out_50hz ? 576 : 480;
cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
@@ -836,10 +855,15 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
- if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
- crop->c.width, crop->c.height, crop->c.left, crop->c.top)) {
- itv->main_rect = crop->c;
+ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ itv->yuv_info.main_rect = crop->c;
return 0;
+ } else {
+ if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+ crop->c.width, crop->c.height, crop->c.left, crop->c.top)) {
+ itv->main_rect = crop->c;
+ return 0;
+ }
}
return -EINVAL;
}
@@ -853,7 +877,10 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
- crop->c = itv->main_rect;
+ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV)
+ crop->c = itv->yuv_info.main_rect;
+ else
+ crop->c = itv->main_rect;
return 0;
}
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -864,7 +891,7 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
case VIDIOC_ENUM_FMT: {
static struct v4l2_fmtdesc formats[] = {
{ 0, 0, 0,
- "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12,
+ "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
{ 0, 0, 0, 0 }
},
{ 1, 0, V4L2_FMT_FLAG_COMPRESSED,
@@ -1043,6 +1070,12 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
itv->main_rect.height = itv->params.height;
ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
720, itv->main_rect.height, 0, 0);
+ itv->yuv_info.main_rect = itv->main_rect;
+ if (!itv->osd_info) {
+ itv->yuv_info.osd_full_w = 720;
+ itv->yuv_info.osd_full_h =
+ itv->is_out_50hz ? 576 : 480;
+ }
}
break;
}
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
index fd1688e4757d..65604dde9726 100644
--- a/drivers/media/video/ivtv/ivtv-irq.c
+++ b/drivers/media/video/ivtv/ivtv-irq.c
@@ -204,7 +204,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
s->sg_pending[idx].dst = buf->dma_handle;
s->sg_pending[idx].src = offset;
s->sg_pending[idx].size = s->buf_size;
- buf->bytesused = (size < s->buf_size) ? size : s->buf_size;
+ buf->bytesused = min(size, s->buf_size);
buf->dma_xfer_cnt = s->dma_xfer_cnt;
s->q_predma.bytesused += buf->bytesused;
@@ -302,8 +302,11 @@ static void dma_post(struct ivtv_stream *s)
void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
{
struct ivtv *itv = s->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ u8 frame = yi->draw_frame;
+ struct yuv_frame_info *f = &yi->new_frame_info[frame];
struct ivtv_buffer *buf;
- u32 y_size = itv->params.height * itv->params.width;
+ u32 y_size = 720 * ((f->src_h + 31) & ~31);
u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET;
int y_done = 0;
int bytes_written = 0;
@@ -311,17 +314,42 @@ void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
int idx = 0;
IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset);
+
+ /* Insert buffer block for YUV if needed */
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && f->offset_y) {
+ if (yi->blanking_dmaptr) {
+ s->sg_pending[idx].src = yi->blanking_dmaptr;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size = 720 * 16;
+ }
+ offset += 720 * 16;
+ idx++;
+ }
+
list_for_each_entry(buf, &s->q_predma.list, list) {
/* YUV UV Offset from Y Buffer */
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && bytes_written >= y_size) {
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done &&
+ (bytes_written + buf->bytesused) >= y_size) {
+ s->sg_pending[idx].src = buf->dma_handle;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size = y_size - bytes_written;
offset = uv_offset;
+ if (s->sg_pending[idx].size != buf->bytesused) {
+ idx++;
+ s->sg_pending[idx].src =
+ buf->dma_handle + s->sg_pending[idx - 1].size;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size =
+ buf->bytesused - s->sg_pending[idx - 1].size;
+ offset += s->sg_pending[idx].size;
+ }
y_done = 1;
+ } else {
+ s->sg_pending[idx].src = buf->dma_handle;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size = buf->bytesused;
+ offset += buf->bytesused;
}
- s->sg_pending[idx].src = buf->dma_handle;
- s->sg_pending[idx].dst = offset;
- s->sg_pending[idx].size = buf->bytesused;
-
- offset += buf->bytesused;
bytes_written += buf->bytesused;
/* Sync SG buffers */
@@ -408,7 +436,7 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s)
s_vbi->sg_pending_size = 0;
s_vbi->dma_xfer_cnt++;
set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
- IVTV_DEBUG_HI_DMA("include DMA for %s\n", s->name);
+ IVTV_DEBUG_HI_DMA("include DMA for %s\n", s_vbi->name);
}
s->dma_xfer_cnt++;
@@ -700,12 +728,15 @@ static void ivtv_irq_dec_data_req(struct ivtv *itv)
ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
- itv->dma_data_req_size = itv->params.width * itv->params.height * 3 / 2;
- itv->dma_data_req_offset = data[1] ? data[1] : yuv_offset[0];
+ itv->dma_data_req_size =
+ 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
+ itv->dma_data_req_offset = data[1];
+ if (atomic_read(&itv->yuv_info.next_dma_frame) >= 0)
+ ivtv_yuv_frame_complete(itv);
s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
}
else {
- itv->dma_data_req_size = data[2] >= 0x10000 ? 0x10000 : data[2];
+ itv->dma_data_req_size = min_t(u32, data[2], 0x10000);
itv->dma_data_req_offset = data[1];
s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
}
@@ -715,6 +746,8 @@ static void ivtv_irq_dec_data_req(struct ivtv *itv)
set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
}
else {
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+ ivtv_yuv_setup_stream_frame(itv);
clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0);
@@ -731,24 +764,26 @@ static void ivtv_irq_vsync(struct ivtv *itv)
* one vsync per frame.
*/
unsigned int frame = read_reg(0x28c0) & 1;
+ struct yuv_playback_info *yi = &itv->yuv_info;
int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame);
+ struct yuv_frame_info *f = &yi->new_frame_info[last_dma_frame];
if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n");
- if (((frame ^ itv->yuv_info.sync_field[last_dma_frame]) == 0 &&
- ((itv->last_vsync_field & 1) ^ itv->yuv_info.sync_field[last_dma_frame])) ||
- (frame != (itv->last_vsync_field & 1) && !itv->yuv_info.frame_interlaced)) {
+ if (((frame ^ f->sync_field) == 0 &&
+ ((itv->last_vsync_field & 1) ^ f->sync_field)) ||
+ (frame != (itv->last_vsync_field & 1) && !f->interlaced)) {
int next_dma_frame = last_dma_frame;
- if (!(itv->yuv_info.frame_interlaced && itv->yuv_info.field_delay[next_dma_frame] && itv->yuv_info.fields_lapsed < 1)) {
- if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&itv->yuv_info.next_fill_frame)) {
+ if (!(f->interlaced && f->delay && yi->fields_lapsed < 1)) {
+ if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&yi->next_fill_frame)) {
write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c);
write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
write_reg(yuv_offset[next_dma_frame] >> 4, 0x834);
write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
- next_dma_frame = (next_dma_frame + 1) & 0x3;
- atomic_set(&itv->yuv_info.next_dma_frame, next_dma_frame);
- itv->yuv_info.fields_lapsed = -1;
+ next_dma_frame = (next_dma_frame + 1) % IVTV_YUV_BUFFERS;
+ atomic_set(&yi->next_dma_frame, next_dma_frame);
+ yi->fields_lapsed = -1;
}
}
}
@@ -781,20 +816,22 @@ static void ivtv_irq_vsync(struct ivtv *itv)
}
/* Check if we need to update the yuv registers */
- if ((itv->yuv_info.yuv_forced_update || itv->yuv_info.new_frame_info[last_dma_frame].update) && last_dma_frame != -1) {
- if (!itv->yuv_info.new_frame_info[last_dma_frame].update)
- last_dma_frame = (last_dma_frame - 1) & 3;
-
- if (itv->yuv_info.new_frame_info[last_dma_frame].src_w) {
- itv->yuv_info.update_frame = last_dma_frame;
- itv->yuv_info.new_frame_info[last_dma_frame].update = 0;
- itv->yuv_info.yuv_forced_update = 0;
+ if ((yi->yuv_forced_update || f->update) && last_dma_frame != -1) {
+ if (!f->update) {
+ last_dma_frame = (u8)(last_dma_frame - 1) % IVTV_YUV_BUFFERS;
+ f = &yi->new_frame_info[last_dma_frame];
+ }
+
+ if (f->src_w) {
+ yi->update_frame = last_dma_frame;
+ f->update = 0;
+ yi->yuv_forced_update = 0;
set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags);
set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
}
}
- itv->yuv_info.fields_lapsed ++;
+ yi->fields_lapsed++;
}
}
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c
index b05436da7136..13a6c374d2db 100644
--- a/drivers/media/video/ivtv/ivtv-mailbox.c
+++ b/drivers/media/video/ivtv/ivtv-mailbox.c
@@ -333,7 +333,7 @@ int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[])
return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res;
}
-int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
+int ivtv_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
{
return ivtv_api(priv, cmd, in, data);
}
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/video/ivtv/ivtv-mailbox.h
index 71a54eef8fc7..6ef12091e3f3 100644
--- a/drivers/media/video/ivtv/ivtv-mailbox.h
+++ b/drivers/media/video/ivtv/ivtv-mailbox.h
@@ -28,6 +28,6 @@ void ivtv_api_get_data(struct ivtv_mailbox_data *mbox, int mb, u32 data[]);
int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]);
int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...);
int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...);
-int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]);
+int ivtv_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]);
#endif
diff --git a/drivers/media/video/ivtv/ivtv-routing.c b/drivers/media/video/ivtv/ivtv-routing.c
index 398bd33033ed..05564919b57f 100644
--- a/drivers/media/video/ivtv/ivtv-routing.c
+++ b/drivers/media/video/ivtv/ivtv-routing.c
@@ -25,6 +25,7 @@
#include "ivtv-routing.h"
#include <media/msp3400.h>
+#include <media/m52790.h>
#include <media/upd64031a.h>
#include <media/upd64083.h>
@@ -32,28 +33,26 @@
settings. */
void ivtv_audio_set_io(struct ivtv *itv)
{
+ const struct ivtv_card_audio_input *in;
struct v4l2_routing route;
- u32 audio_input;
- int mux_input;
/* Determine which input to use */
- if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
- audio_input = itv->card->radio_input.audio_input;
- mux_input = itv->card->radio_input.muxer_input;
- } else {
- audio_input = itv->card->audio_inputs[itv->audio_input].audio_input;
- mux_input = itv->card->audio_inputs[itv->audio_input].muxer_input;
- }
+ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
+ in = &itv->card->radio_input;
+ else
+ in = &itv->card->audio_inputs[itv->audio_input];
/* handle muxer chips */
- route.input = mux_input;
+ route.input = in->muxer_input;
route.output = 0;
+ if (itv->card->hw_muxer & IVTV_HW_M52790)
+ route.output = M52790_OUT_STEREO;
ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
- route.input = audio_input;
- if (itv->card->hw_audio & IVTV_HW_MSP34XX) {
+ route.input = in->audio_input;
+ route.output = 0;
+ if (itv->card->hw_audio & IVTV_HW_MSP34XX)
route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
- }
ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route);
}
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index 74fb0e021979..24d98ecf35ad 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -43,7 +43,7 @@
#include "ivtv-cards.h"
#include "ivtv-streams.h"
-static struct file_operations ivtv_v4l2_enc_fops = {
+static const struct file_operations ivtv_v4l2_enc_fops = {
.owner = THIS_MODULE,
.read = ivtv_v4l2_read,
.write = ivtv_v4l2_write,
@@ -53,7 +53,7 @@ static struct file_operations ivtv_v4l2_enc_fops = {
.poll = ivtv_v4l2_enc_poll,
};
-static struct file_operations ivtv_v4l2_dec_fops = {
+static const struct file_operations ivtv_v4l2_dec_fops = {
.owner = THIS_MODULE,
.read = ivtv_v4l2_read,
.write = ivtv_v4l2_write,
@@ -572,10 +572,10 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
clear_bit(IVTV_F_I_EOS, &itv->i_flags);
/* Initialize Digitizer for Capture */
- itv->video_dec_func(itv, VIDIOC_STREAMOFF, 0);
+ itv->video_dec_func(itv, VIDIOC_STREAMOFF, NULL);
ivtv_msleep_timeout(300, 1);
ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
- itv->video_dec_func(itv, VIDIOC_STREAMON, 0);
+ itv->video_dec_func(itv, VIDIOC_STREAMON, NULL);
}
/* begin_capture */
@@ -661,27 +661,12 @@ int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset);
- /* Clear Streamoff */
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
- /* Initialize Decoder */
- /* Reprogram Decoder YUV Buffers for YUV */
- write_reg(yuv_offset[0] >> 4, 0x82c);
- write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
- write_reg(yuv_offset[0] >> 4, 0x834);
- write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
-
- write_reg_sync(0x00000000 | (0x0c << 16) | (0x0b << 8), 0x2d24);
-
- write_reg_sync(0x00108080, 0x2898);
- /* Enable YUV decoder output */
- write_reg_sync(0x01, IVTV_REG_VDM);
- }
-
ivtv_setup_v4l2_decode_stream(s);
/* set dma size to 65536 bytes */
ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536);
+ /* Clear Streamoff */
clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
/* Zero out decoder counters */
diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h
index d050de2a7229..0f1d4cc4b4d9 100644
--- a/drivers/media/video/ivtv/ivtv-version.h
+++ b/drivers/media/video/ivtv/ivtv-version.h
@@ -22,7 +22,7 @@
#define IVTV_DRIVER_NAME "ivtv"
#define IVTV_DRIVER_VERSION_MAJOR 1
-#define IVTV_DRIVER_VERSION_MINOR 1
+#define IVTV_DRIVER_VERSION_MINOR 2
#define IVTV_DRIVER_VERSION_PATCHLEVEL 0
#define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL)
diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c
index 9091c4837bbc..85183480a225 100644
--- a/drivers/media/video/ivtv/ivtv-yuv.c
+++ b/drivers/media/video/ivtv/ivtv-yuv.c
@@ -22,32 +22,37 @@
#include "ivtv-udma.h"
#include "ivtv-yuv.h"
-const u32 yuv_offset[4] = {
- IVTV_YUV_BUFFER_OFFSET,
- IVTV_YUV_BUFFER_OFFSET_1,
- IVTV_YUV_BUFFER_OFFSET_2,
- IVTV_YUV_BUFFER_OFFSET_3
+/* YUV buffer offsets */
+const u32 yuv_offset[IVTV_YUV_BUFFERS] = {
+ 0x001a8600,
+ 0x00240400,
+ 0x002d8200,
+ 0x00370000,
+ 0x00029000,
+ 0x000C0E00,
+ 0x006B0400,
+ 0x00748200
};
static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
- struct ivtv_dma_frame *args)
+ struct ivtv_dma_frame *args)
{
struct ivtv_dma_page_info y_dma;
struct ivtv_dma_page_info uv_dma;
-
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ u8 frame = yi->draw_frame;
+ struct yuv_frame_info *f = &yi->new_frame_info[frame];
int i;
int y_pages, uv_pages;
-
unsigned long y_buffer_offset, uv_buffer_offset;
int y_decode_height, uv_decode_height, y_size;
- int frame = atomic_read(&itv->yuv_info.next_fill_frame);
y_buffer_offset = IVTV_DECODER_OFFSET + yuv_offset[frame];
uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET;
- y_decode_height = uv_decode_height = args->src.height + args->src.top;
+ y_decode_height = uv_decode_height = f->src_h + f->src_y;
- if (y_decode_height < 512-16)
+ if (f->offset_y)
y_buffer_offset += 720 * 16;
if (y_decode_height & 15)
@@ -60,8 +65,9 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
/* Still in USE */
if (dma->SG_length || dma->page_count) {
- IVTV_DEBUG_WARN("prep_user_dma: SG_length %d page_count %d still full?\n",
- dma->SG_length, dma->page_count);
+ IVTV_DEBUG_WARN
+ ("prep_user_dma: SG_length %d page_count %d still full?\n",
+ dma->SG_length, dma->page_count);
return -EBUSY;
}
@@ -77,8 +83,9 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
dma->page_count = y_dma.page_count + uv_dma.page_count;
if (y_pages + uv_pages != dma->page_count) {
- IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
- y_pages + uv_pages, dma->page_count);
+ IVTV_DEBUG_WARN
+ ("failed to map user pages, returned %d instead of %d\n",
+ y_pages + uv_pages, dma->page_count);
for (i = 0; i < dma->page_count; i++) {
put_page(dma->map[i]);
@@ -99,16 +106,14 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
/* Fill SG Array with new values */
- ivtv_udma_fill_sg_array (dma, y_buffer_offset, uv_buffer_offset, y_size);
+ ivtv_udma_fill_sg_array(dma, y_buffer_offset, uv_buffer_offset, y_size);
/* If we've offset the y plane, ensure top area is blanked */
- if (args->src.height + args->src.top < 512-16) {
- if (itv->yuv_info.blanking_dmaptr) {
- dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16);
- dma->SGarray[dma->SG_length].src = cpu_to_le32(itv->yuv_info.blanking_dmaptr);
- dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DECODER_OFFSET + yuv_offset[frame]);
- dma->SG_length++;
- }
+ if (f->offset_y && yi->blanking_dmaptr) {
+ dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16);
+ dma->SGarray[dma->SG_length].src = cpu_to_le32(yi->blanking_dmaptr);
+ dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DECODER_OFFSET + yuv_offset[frame]);
+ dma->SG_length++;
}
/* Tag SG Array with Interrupt Bit */
@@ -121,11 +126,11 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
/* We rely on a table held in the firmware - Quick check. */
int ivtv_yuv_filter_check(struct ivtv *itv)
{
- int i, offset_y, offset_uv;
+ int i, y, uv;
- for (i=0, offset_y = 16, offset_uv = 4; i<16; i++, offset_y += 24, offset_uv += 12) {
- if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + offset_y) != i << 16) ||
- (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + offset_uv) != i << 16)) {
+ for (i = 0, y = 16, uv = 4; i < 16; i++, y += 24, uv += 12) {
+ if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + y) != i << 16) ||
+ (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + uv) != i << 16)) {
IVTV_WARN ("YUV filter table not found in firmware.\n");
return -1;
}
@@ -135,69 +140,67 @@ int ivtv_yuv_filter_check(struct ivtv *itv)
static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2)
{
- int filter_index, filter_line;
+ u32 i, line;
/* If any filter is -1, then don't update it */
if (h_filter > -1) {
- if (h_filter > 4) h_filter = 4;
- filter_index = h_filter * 384;
- filter_line = 0;
- while (filter_line < 16) {
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02804);
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0281c);
- filter_index += 4;
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02808);
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02820);
- filter_index += 4;
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0280c);
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02824);
- filter_index += 4;
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02810);
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02828);
- filter_index += 4;
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02814);
- write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0282c);
- filter_index += 8;
+ if (h_filter > 4)
+ h_filter = 4;
+ i = IVTV_YUV_HORIZONTAL_FILTER_OFFSET + (h_filter * 384);
+ for (line = 0; line < 16; line++) {
+ write_reg(read_dec(i), 0x02804);
+ write_reg(read_dec(i), 0x0281c);
+ i += 4;
+ write_reg(read_dec(i), 0x02808);
+ write_reg(read_dec(i), 0x02820);
+ i += 4;
+ write_reg(read_dec(i), 0x0280c);
+ write_reg(read_dec(i), 0x02824);
+ i += 4;
+ write_reg(read_dec(i), 0x02810);
+ write_reg(read_dec(i), 0x02828);
+ i += 4;
+ write_reg(read_dec(i), 0x02814);
+ write_reg(read_dec(i), 0x0282c);
+ i += 8;
write_reg(0, 0x02818);
write_reg(0, 0x02830);
- filter_line ++;
}
- IVTV_DEBUG_YUV("h_filter -> %d\n",h_filter);
+ IVTV_DEBUG_YUV("h_filter -> %d\n", h_filter);
}
if (v_filter_1 > -1) {
- if (v_filter_1 > 4) v_filter_1 = 4;
- filter_index = v_filter_1 * 192;
- filter_line = 0;
- while (filter_line < 16) {
- write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02900);
- filter_index += 4;
- write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02904);
- filter_index += 8;
+ if (v_filter_1 > 4)
+ v_filter_1 = 4;
+ i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_1 * 192);
+ for (line = 0; line < 16; line++) {
+ write_reg(read_dec(i), 0x02900);
+ i += 4;
+ write_reg(read_dec(i), 0x02904);
+ i += 8;
write_reg(0, 0x02908);
- filter_line ++;
}
- IVTV_DEBUG_YUV("v_filter_1 -> %d\n",v_filter_1);
+ IVTV_DEBUG_YUV("v_filter_1 -> %d\n", v_filter_1);
}
if (v_filter_2 > -1) {
- if (v_filter_2 > 4) v_filter_2 = 4;
- filter_index = v_filter_2 * 192;
- filter_line = 0;
- while (filter_line < 16) {
- write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x0290c);
- filter_index += 4;
- write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02910);
- filter_index += 8;
+ if (v_filter_2 > 4)
+ v_filter_2 = 4;
+ i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_2 * 192);
+ for (line = 0; line < 16; line++) {
+ write_reg(read_dec(i), 0x0290c);
+ i += 4;
+ write_reg(read_dec(i), 0x02910);
+ i += 8;
write_reg(0, 0x02914);
- filter_line ++;
}
- IVTV_DEBUG_YUV("v_filter_2 -> %d\n",v_filter_2);
+ IVTV_DEBUG_YUV("v_filter_2 -> %d\n", v_filter_2);
}
}
-static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *window)
+static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *f)
{
+ struct yuv_playback_info *yi = &itv->yuv_info;
u32 reg_2834, reg_2838, reg_283c;
u32 reg_2844, reg_2854, reg_285c;
u32 reg_2864, reg_2874, reg_2890;
@@ -206,18 +209,19 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *
int h_filter;
u32 master_width;
- IVTV_DEBUG_WARN( "Need to adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n",
- window->tru_w, window->src_w, window->dst_w,window->src_x, window->dst_x);
+ IVTV_DEBUG_WARN
+ ("Adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n",
+ f->tru_w, f->src_w, f->dst_w, f->src_x, f->dst_x);
/* How wide is the src image */
- x_cutoff = window->src_w + window->src_x;
+ x_cutoff = f->src_w + f->src_x;
/* Set the display width */
- reg_2834 = window->dst_w;
+ reg_2834 = f->dst_w;
reg_2838 = reg_2834;
/* Set the display position */
- reg_2890 = window->dst_x;
+ reg_2890 = f->dst_x;
/* Index into the image horizontally */
reg_2870 = 0;
@@ -228,32 +232,31 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *
Gradually adjust the offset to avoid the video 'snapping'
left/right if it gets dragged through this region.
Only do this if osd is full width. */
- if (window->vis_w == 720) {
- if ((window->tru_x - window->pan_x > -1) && (window->tru_x - window->pan_x <= 40) && (window->dst_w >= 680)){
- reg_2870 = 10 - (window->tru_x - window->pan_x) / 4;
- }
- else if ((window->tru_x - window->pan_x < 0) && (window->tru_x - window->pan_x >= -20) && (window->dst_w >= 660)) {
- reg_2870 = (10 + (window->tru_x - window->pan_x) / 2);
- }
+ if (f->vis_w == 720) {
+ if ((f->tru_x - f->pan_x > -1) && (f->tru_x - f->pan_x <= 40) && (f->dst_w >= 680))
+ reg_2870 = 10 - (f->tru_x - f->pan_x) / 4;
+ else if ((f->tru_x - f->pan_x < 0) && (f->tru_x - f->pan_x >= -20) && (f->dst_w >= 660))
+ reg_2870 = (10 + (f->tru_x - f->pan_x) / 2);
- if (window->dst_w >= window->src_w)
+ if (f->dst_w >= f->src_w)
reg_2870 = reg_2870 << 16 | reg_2870;
else
reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1);
}
- if (window->dst_w < window->src_w)
+ if (f->dst_w < f->src_w)
reg_2870 = 0x000d000e - reg_2870;
else
reg_2870 = 0x0012000e - reg_2870;
/* We're also using 2870 to shift the image left (src_x & negative dst_x) */
- reg_2870_offset = (window->src_x*((window->dst_w << 21)/window->src_w))>>19;
+ reg_2870_offset = (f->src_x * ((f->dst_w << 21) / f->src_w)) >> 19;
- if (window->dst_w >= window->src_w) {
+ if (f->dst_w >= f->src_w) {
x_cutoff &= ~1;
- master_width = (window->src_w * 0x00200000) / (window->dst_w);
- if (master_width * window->dst_w != window->src_w * 0x00200000) master_width ++;
+ master_width = (f->src_w * 0x00200000) / (f->dst_w);
+ if (master_width * f->dst_w != f->src_w * 0x00200000)
+ master_width++;
reg_2834 = (reg_2834 << 16) | x_cutoff;
reg_2838 = (reg_2838 << 16) | x_cutoff;
reg_283c = master_width >> 2;
@@ -264,17 +267,17 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *
/* We also need to factor in the scaling
(src_w - dst_w) / (src_w / 4) */
- if (window->dst_w > window->src_w)
- reg_2870_base = ((window->dst_w - window->src_w)<<16) / (window->src_w <<14);
+ if (f->dst_w > f->src_w)
+ reg_2870_base = ((f->dst_w - f->src_w)<<16) / (f->src_w <<14);
else
reg_2870_base = 0;
reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 2) + (reg_2870_base << 17 | reg_2870_base);
reg_2874 = 0;
- }
- else if (window->dst_w < window->src_w / 2) {
- master_width = (window->src_w * 0x00080000) / window->dst_w;
- if (master_width * window->dst_w != window->src_w * 0x00080000) master_width ++;
+ } else if (f->dst_w < f->src_w / 2) {
+ master_width = (f->src_w * 0x00080000) / f->dst_w;
+ if (master_width * f->dst_w != f->src_w * 0x00080000)
+ master_width++;
reg_2834 = (reg_2834 << 16) | x_cutoff;
reg_2838 = (reg_2838 << 16) | x_cutoff;
reg_283c = master_width >> 2;
@@ -282,13 +285,13 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *
reg_2854 = master_width;
reg_285c = master_width >> 1;
reg_2864 = master_width >> 1;
- reg_2870 += (((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset);
- reg_2870 += (5 - (((window->src_w + window->src_w / 2) - 1) / window->dst_w)) << 16;
+ reg_2870 += ((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset;
+ reg_2870 += (5 - (((f->src_w + f->src_w / 2) - 1) / f->dst_w)) << 16;
reg_2874 = 0x00000012;
- }
- else {
- master_width = (window->src_w * 0x00100000) / window->dst_w;
- if (master_width * window->dst_w != window->src_w * 0x00100000) master_width ++;
+ } else {
+ master_width = (f->src_w * 0x00100000) / f->dst_w;
+ if (master_width * f->dst_w != f->src_w * 0x00100000)
+ master_width++;
reg_2834 = (reg_2834 << 16) | x_cutoff;
reg_2838 = (reg_2838 << 16) | x_cutoff;
reg_283c = master_width >> 2;
@@ -296,62 +299,70 @@ static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *
reg_2854 = master_width;
reg_285c = master_width >> 1;
reg_2864 = master_width >> 1;
- reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1);
- reg_2870 += (5 - (((window->src_w * 3) - 1) / window->dst_w)) << 16;
+ reg_2870 += ((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1;
+ reg_2870 += (5 - (((f->src_w * 3) - 1) / f->dst_w)) << 16;
reg_2874 = 0x00000001;
}
/* Select the horizontal filter */
- if (window->src_w == window->dst_w) {
+ if (f->src_w == f->dst_w) {
/* An exact size match uses filter 0 */
h_filter = 0;
- }
- else {
+ } else {
/* Figure out which filter to use */
- h_filter = ((window->src_w << 16) / window->dst_w) >> 15;
+ h_filter = ((f->src_w << 16) / f->dst_w) >> 15;
h_filter = (h_filter >> 1) + (h_filter & 1);
/* Only an exact size match can use filter 0 */
- if (h_filter == 0) h_filter = 1;
+ h_filter += !h_filter;
}
write_reg(reg_2834, 0x02834);
write_reg(reg_2838, 0x02838);
- IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",itv->yuv_info.reg_2834, reg_2834, itv->yuv_info.reg_2838, reg_2838);
+ IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",
+ yi->reg_2834, reg_2834, yi->reg_2838, reg_2838);
write_reg(reg_283c, 0x0283c);
write_reg(reg_2844, 0x02844);
- IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",itv->yuv_info.reg_283c, reg_283c, itv->yuv_info.reg_2844, reg_2844);
+ IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",
+ yi->reg_283c, reg_283c, yi->reg_2844, reg_2844);
write_reg(0x00080514, 0x02840);
write_reg(0x00100514, 0x02848);
- IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",itv->yuv_info.reg_2840, 0x00080514, itv->yuv_info.reg_2848, 0x00100514);
+ IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",
+ yi->reg_2840, 0x00080514, yi->reg_2848, 0x00100514);
write_reg(reg_2854, 0x02854);
- IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",itv->yuv_info.reg_2854, reg_2854);
+ IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",
+ yi->reg_2854, reg_2854);
write_reg(reg_285c, 0x0285c);
write_reg(reg_2864, 0x02864);
- IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",itv->yuv_info.reg_285c, reg_285c, itv->yuv_info.reg_2864, reg_2864);
+ IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",
+ yi->reg_285c, reg_285c, yi->reg_2864, reg_2864);
write_reg(reg_2874, 0x02874);
- IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",itv->yuv_info.reg_2874, reg_2874);
+ IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",
+ yi->reg_2874, reg_2874);
write_reg(reg_2870, 0x02870);
- IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",itv->yuv_info.reg_2870, reg_2870);
+ IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",
+ yi->reg_2870, reg_2870);
- write_reg( reg_2890,0x02890);
- IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",itv->yuv_info.reg_2890, reg_2890);
+ write_reg(reg_2890, 0x02890);
+ IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",
+ yi->reg_2890, reg_2890);
/* Only update the filter if we really need to */
- if (h_filter != itv->yuv_info.h_filter) {
- ivtv_yuv_filter (itv,h_filter,-1,-1);
- itv->yuv_info.h_filter = h_filter;
+ if (h_filter != yi->h_filter) {
+ ivtv_yuv_filter(itv, h_filter, -1, -1);
+ yi->h_filter = h_filter;
}
}
-static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *window)
+static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *f)
{
+ struct yuv_playback_info *yi = &itv->yuv_info;
u32 master_height;
u32 reg_2918, reg_291c, reg_2920, reg_2928;
u32 reg_2930, reg_2934, reg_293c;
@@ -359,69 +370,59 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi
u32 reg_2950, reg_2954, reg_2958, reg_295c;
u32 reg_2960, reg_2964, reg_2968, reg_296c;
u32 reg_289c;
- u32 src_y_major_y, src_y_minor_y;
- u32 src_y_major_uv, src_y_minor_uv;
+ u32 src_major_y, src_minor_y;
+ u32 src_major_uv, src_minor_uv;
u32 reg_2964_base, reg_2968_base;
int v_filter_1, v_filter_2;
- IVTV_DEBUG_WARN("Need to adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n",
- window->tru_h, window->src_h, window->dst_h,window->src_y, window->dst_y);
+ IVTV_DEBUG_WARN
+ ("Adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n",
+ f->tru_h, f->src_h, f->dst_h, f->src_y, f->dst_y);
/* What scaling mode is being used... */
- if (window->interlaced_y) {
- IVTV_DEBUG_YUV("Scaling mode Y: Interlaced\n");
- }
- else {
- IVTV_DEBUG_YUV("Scaling mode Y: Progressive\n");
- }
+ IVTV_DEBUG_YUV("Scaling mode Y: %s\n",
+ f->interlaced_y ? "Interlaced" : "Progressive");
- if (window->interlaced_uv) {
- IVTV_DEBUG_YUV("Scaling mode UV: Interlaced\n");
- }
- else {
- IVTV_DEBUG_YUV("Scaling mode UV: Progressive\n");
- }
+ IVTV_DEBUG_YUV("Scaling mode UV: %s\n",
+ f->interlaced_uv ? "Interlaced" : "Progressive");
/* What is the source video being treated as... */
- if (itv->yuv_info.frame_interlaced) {
- IVTV_DEBUG_WARN("Source video: Interlaced\n");
- }
- else {
- IVTV_DEBUG_WARN("Source video: Non-interlaced\n");
- }
+ IVTV_DEBUG_WARN("Source video: %s\n",
+ f->interlaced ? "Interlaced" : "Progressive");
/* We offset into the image using two different index methods, so split
the y source coord into two parts. */
- if (window->src_y < 8) {
- src_y_minor_uv = window->src_y;
- src_y_major_uv = 0;
- }
- else {
- src_y_minor_uv = 8;
- src_y_major_uv = window->src_y - 8;
+ if (f->src_y < 8) {
+ src_minor_uv = f->src_y;
+ src_major_uv = 0;
+ } else {
+ src_minor_uv = 8;
+ src_major_uv = f->src_y - 8;
}
- src_y_minor_y = src_y_minor_uv;
- src_y_major_y = src_y_major_uv;
+ src_minor_y = src_minor_uv;
+ src_major_y = src_major_uv;
- if (window->offset_y) src_y_minor_y += 16;
+ if (f->offset_y)
+ src_minor_y += 16;
- if (window->interlaced_y)
- reg_2918 = (window->dst_h << 16) | (window->src_h + src_y_minor_y);
+ if (f->interlaced_y)
+ reg_2918 = (f->dst_h << 16) | (f->src_h + src_minor_y);
else
- reg_2918 = (window->dst_h << 16) | ((window->src_h + src_y_minor_y) << 1);
+ reg_2918 = (f->dst_h << 16) | ((f->src_h + src_minor_y) << 1);
- if (window->interlaced_uv)
- reg_291c = (window->dst_h << 16) | ((window->src_h + src_y_minor_uv) >> 1);
+ if (f->interlaced_uv)
+ reg_291c = (f->dst_h << 16) | ((f->src_h + src_minor_uv) >> 1);
else
- reg_291c = (window->dst_h << 16) | (window->src_h + src_y_minor_uv);
+ reg_291c = (f->dst_h << 16) | (f->src_h + src_minor_uv);
- reg_2964_base = (src_y_minor_y * ((window->dst_h << 16)/window->src_h)) >> 14;
- reg_2968_base = (src_y_minor_uv * ((window->dst_h << 16)/window->src_h)) >> 14;
+ reg_2964_base = (src_minor_y * ((f->dst_h << 16) / f->src_h)) >> 14;
+ reg_2968_base = (src_minor_uv * ((f->dst_h << 16) / f->src_h)) >> 14;
- if (window->dst_h / 2 >= window->src_h && !window->interlaced_y) {
- master_height = (window->src_h * 0x00400000) / window->dst_h;
- if ((window->src_h * 0x00400000) - (master_height * window->dst_h) >= window->dst_h / 2) master_height ++;
+ if (f->dst_h / 2 >= f->src_h && !f->interlaced_y) {
+ master_height = (f->src_h * 0x00400000) / f->dst_h;
+ if ((f->src_h * 0x00400000) - (master_height * f->dst_h) >= f->dst_h / 2)
+ master_height++;
reg_2920 = master_height >> 2;
reg_2928 = master_height >> 3;
reg_2930 = master_height;
@@ -429,45 +430,42 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi
reg_2964_base >>= 3;
reg_2968_base >>= 3;
reg_296c = 0x00000000;
- }
- else if (window->dst_h >= window->src_h) {
- master_height = (window->src_h * 0x00400000) / window->dst_h;
+ } else if (f->dst_h >= f->src_h) {
+ master_height = (f->src_h * 0x00400000) / f->dst_h;
master_height = (master_height >> 1) + (master_height & 1);
reg_2920 = master_height >> 2;
reg_2928 = master_height >> 2;
reg_2930 = master_height;
reg_2940 = master_height >> 1;
reg_296c = 0x00000000;
- if (window->interlaced_y) {
+ if (f->interlaced_y) {
reg_2964_base >>= 3;
- }
- else {
- reg_296c ++;
+ } else {
+ reg_296c++;
reg_2964_base >>= 2;
}
- if (window->interlaced_uv) reg_2928 >>= 1;
+ if (f->interlaced_uv)
+ reg_2928 >>= 1;
reg_2968_base >>= 3;
- }
- else if (window->dst_h >= window->src_h / 2) {
- master_height = (window->src_h * 0x00200000) / window->dst_h;
+ } else if (f->dst_h >= f->src_h / 2) {
+ master_height = (f->src_h * 0x00200000) / f->dst_h;
master_height = (master_height >> 1) + (master_height & 1);
reg_2920 = master_height >> 2;
reg_2928 = master_height >> 2;
reg_2930 = master_height;
reg_2940 = master_height;
reg_296c = 0x00000101;
- if (window->interlaced_y) {
+ if (f->interlaced_y) {
reg_2964_base >>= 2;
- }
- else {
- reg_296c ++;
+ } else {
+ reg_296c++;
reg_2964_base >>= 1;
}
- if (window->interlaced_uv) reg_2928 >>= 1;
+ if (f->interlaced_uv)
+ reg_2928 >>= 1;
reg_2968_base >>= 2;
- }
- else {
- master_height = (window->src_h * 0x00100000) / window->dst_h;
+ } else {
+ master_height = (f->src_h * 0x00100000) / f->dst_h;
master_height = (master_height >> 1) + (master_height & 1);
reg_2920 = master_height >> 2;
reg_2928 = master_height >> 2;
@@ -480,13 +478,12 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi
/* FIXME These registers change depending on scaled / unscaled output
We really need to work out what they should be */
- if (window->src_h == window->dst_h){
+ if (f->src_h == f->dst_h) {
reg_2934 = 0x00020000;
reg_293c = 0x00100000;
reg_2944 = 0x00040000;
reg_294c = 0x000b0000;
- }
- else {
+ } else {
reg_2934 = 0x00000FF0;
reg_293c = 0x00000FF0;
reg_2944 = 0x00000FF0;
@@ -494,34 +491,36 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi
}
/* The first line to be displayed */
- reg_2950 = 0x00010000 + src_y_major_y;
- if (window->interlaced_y) reg_2950 += 0x00010000;
+ reg_2950 = 0x00010000 + src_major_y;
+ if (f->interlaced_y)
+ reg_2950 += 0x00010000;
reg_2954 = reg_2950 + 1;
- reg_2958 = 0x00010000 + (src_y_major_y >> 1);
- if (window->interlaced_uv) reg_2958 += 0x00010000;
+ reg_2958 = 0x00010000 + (src_major_y >> 1);
+ if (f->interlaced_uv)
+ reg_2958 += 0x00010000;
reg_295c = reg_2958 + 1;
- if (itv->yuv_info.decode_height == 480)
+ if (yi->decode_height == 480)
reg_289c = 0x011e0017;
else
reg_289c = 0x01500017;
- if (window->dst_y < 0)
- reg_289c = (reg_289c - ((window->dst_y & ~1)<<15))-(window->dst_y >>1);
+ if (f->dst_y < 0)
+ reg_289c = (reg_289c - ((f->dst_y & ~1)<<15))-(f->dst_y >>1);
else
- reg_289c = (reg_289c + ((window->dst_y & ~1)<<15))+(window->dst_y >>1);
+ reg_289c = (reg_289c + ((f->dst_y & ~1)<<15))+(f->dst_y >>1);
/* How much of the source to decode.
Take into account the source offset */
- reg_2960 = ((src_y_minor_y + window->src_h + src_y_major_y) - 1 ) |
- ((((src_y_minor_uv + window->src_h + src_y_major_uv) - 1) & ~1) << 15);
+ reg_2960 = ((src_minor_y + f->src_h + src_major_y) - 1) |
+ (((src_minor_uv + f->src_h + src_major_uv - 1) & ~1) << 15);
/* Calculate correct value for register 2964 */
- if (window->src_h == window->dst_h)
+ if (f->src_h == f->dst_h) {
reg_2964 = 1;
- else {
- reg_2964 = 2 + ((window->dst_h << 1) / window->src_h);
+ } else {
+ reg_2964 = 2 + ((f->dst_h << 1) / f->src_h);
reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1);
}
reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1);
@@ -536,283 +535,246 @@ static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *wi
/* Deviate further from what it should be. I find the flicker headache
inducing so try to reduce it slightly. Leave 2968 as-is otherwise
colours foul. */
- if ((reg_2964 != 0x00010001) && (window->dst_h / 2 <= window->src_h))
- reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF)/2);
+ if ((reg_2964 != 0x00010001) && (f->dst_h / 2 <= f->src_h))
+ reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF) / 2);
- if (!window->interlaced_y) reg_2964 -= 0x00010001;
- if (!window->interlaced_uv) reg_2968 -= 0x00010001;
+ if (!f->interlaced_y)
+ reg_2964 -= 0x00010001;
+ if (!f->interlaced_uv)
+ reg_2968 -= 0x00010001;
reg_2964 += ((reg_2964_base << 16) | reg_2964_base);
reg_2968 += ((reg_2968_base << 16) | reg_2968_base);
/* Select the vertical filter */
- if (window->src_h == window->dst_h) {
+ if (f->src_h == f->dst_h) {
/* An exact size match uses filter 0/1 */
v_filter_1 = 0;
v_filter_2 = 1;
- }
- else {
+ } else {
/* Figure out which filter to use */
- v_filter_1 = ((window->src_h << 16) / window->dst_h) >> 15;
+ v_filter_1 = ((f->src_h << 16) / f->dst_h) >> 15;
v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
/* Only an exact size match can use filter 0 */
- if (v_filter_1 == 0) v_filter_1 = 1;
+ v_filter_1 += !v_filter_1;
v_filter_2 = v_filter_1;
}
write_reg(reg_2934, 0x02934);
write_reg(reg_293c, 0x0293c);
- IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",itv->yuv_info.reg_2934, reg_2934, itv->yuv_info.reg_293c, reg_293c);
+ IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",
+ yi->reg_2934, reg_2934, yi->reg_293c, reg_293c);
write_reg(reg_2944, 0x02944);
write_reg(reg_294c, 0x0294c);
- IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",itv->yuv_info.reg_2944, reg_2944, itv->yuv_info.reg_294c, reg_294c);
+ IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",
+ yi->reg_2944, reg_2944, yi->reg_294c, reg_294c);
/* Ensure 2970 is 0 (does it ever change ?) */
/* write_reg(0,0x02970); */
-/* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n",itv->yuv_info.reg_2970, 0); */
+/* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n", yi->reg_2970, 0); */
write_reg(reg_2930, 0x02938);
write_reg(reg_2930, 0x02930);
- IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",itv->yuv_info.reg_2930, reg_2930, itv->yuv_info.reg_2938, reg_2930);
+ IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",
+ yi->reg_2930, reg_2930, yi->reg_2938, reg_2930);
write_reg(reg_2928, 0x02928);
- write_reg(reg_2928+0x514, 0x0292C);
- IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",itv->yuv_info.reg_2928, reg_2928, itv->yuv_info.reg_292c, reg_2928+0x514);
+ write_reg(reg_2928 + 0x514, 0x0292C);
+ IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",
+ yi->reg_2928, reg_2928, yi->reg_292c, reg_2928 + 0x514);
write_reg(reg_2920, 0x02920);
- write_reg(reg_2920+0x514, 0x02924);
- IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",itv->yuv_info.reg_2920, reg_2920, itv->yuv_info.reg_2924, 0x514+reg_2920);
+ write_reg(reg_2920 + 0x514, 0x02924);
+ IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",
+ yi->reg_2920, reg_2920, yi->reg_2924, reg_2920 + 0x514);
- write_reg (reg_2918,0x02918);
- write_reg (reg_291c,0x0291C);
- IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",itv->yuv_info.reg_2918,reg_2918,itv->yuv_info.reg_291c,reg_291c);
+ write_reg(reg_2918, 0x02918);
+ write_reg(reg_291c, 0x0291C);
+ IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",
+ yi->reg_2918, reg_2918, yi->reg_291c, reg_291c);
write_reg(reg_296c, 0x0296c);
- IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",itv->yuv_info.reg_296c, reg_296c);
+ IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",
+ yi->reg_296c, reg_296c);
write_reg(reg_2940, 0x02948);
write_reg(reg_2940, 0x02940);
- IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",itv->yuv_info.reg_2940, reg_2940, itv->yuv_info.reg_2948, reg_2940);
+ IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",
+ yi->reg_2940, reg_2940, yi->reg_2948, reg_2940);
write_reg(reg_2950, 0x02950);
write_reg(reg_2954, 0x02954);
- IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",itv->yuv_info.reg_2950, reg_2950, itv->yuv_info.reg_2954, reg_2954);
+ IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",
+ yi->reg_2950, reg_2950, yi->reg_2954, reg_2954);
write_reg(reg_2958, 0x02958);
write_reg(reg_295c, 0x0295C);
- IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",itv->yuv_info.reg_2958, reg_2958, itv->yuv_info.reg_295c, reg_295c);
+ IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",
+ yi->reg_2958, reg_2958, yi->reg_295c, reg_295c);
write_reg(reg_2960, 0x02960);
- IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",itv->yuv_info.reg_2960, reg_2960);
+ IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",
+ yi->reg_2960, reg_2960);
write_reg(reg_2964, 0x02964);
write_reg(reg_2968, 0x02968);
- IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",itv->yuv_info.reg_2964, reg_2964, itv->yuv_info.reg_2968, reg_2968);
+ IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",
+ yi->reg_2964, reg_2964, yi->reg_2968, reg_2968);
- write_reg( reg_289c,0x0289c);
- IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",itv->yuv_info.reg_289c, reg_289c);
+ write_reg(reg_289c, 0x0289c);
+ IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",
+ yi->reg_289c, reg_289c);
/* Only update filter 1 if we really need to */
- if (v_filter_1 != itv->yuv_info.v_filter_1) {
- ivtv_yuv_filter (itv,-1,v_filter_1,-1);
- itv->yuv_info.v_filter_1 = v_filter_1;
+ if (v_filter_1 != yi->v_filter_1) {
+ ivtv_yuv_filter(itv, -1, v_filter_1, -1);
+ yi->v_filter_1 = v_filter_1;
}
/* Only update filter 2 if we really need to */
- if (v_filter_2 != itv->yuv_info.v_filter_2) {
- ivtv_yuv_filter (itv,-1,-1,v_filter_2);
- itv->yuv_info.v_filter_2 = v_filter_2;
+ if (v_filter_2 != yi->v_filter_2) {
+ ivtv_yuv_filter(itv, -1, -1, v_filter_2);
+ yi->v_filter_2 = v_filter_2;
}
-
}
/* Modify the supplied coordinate information to fit the visible osd area */
-static u32 ivtv_yuv_window_setup (struct ivtv *itv, struct yuv_frame_info *window)
+static u32 ivtv_yuv_window_setup(struct ivtv *itv, struct yuv_frame_info *f)
{
- int osd_crop, lace_threshold;
+ struct yuv_frame_info *of = &itv->yuv_info.old_frame_info;
+ int osd_crop;
u32 osd_scale;
u32 yuv_update = 0;
- lace_threshold = itv->yuv_info.lace_threshold;
- if (lace_threshold < 0)
- lace_threshold = itv->yuv_info.decode_height - 1;
-
- /* Work out the lace settings */
- switch (itv->yuv_info.lace_mode) {
- case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */
- itv->yuv_info.frame_interlaced = 0;
- if (window->tru_h < 512 || (window->tru_h > 576 && window->tru_h < 1021))
- window->interlaced_y = 0;
- else
- window->interlaced_y = 1;
-
- if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2))
- window->interlaced_uv = 0;
- else
- window->interlaced_uv = 1;
- break;
-
- case IVTV_YUV_MODE_AUTO:
- if (window->tru_h <= lace_threshold || window->tru_h > 576 || window->tru_w > 720){
- itv->yuv_info.frame_interlaced = 0;
- if ((window->tru_h < 512) ||
- (window->tru_h > 576 && window->tru_h < 1021) ||
- (window->tru_w > 720 && window->tru_h < 1021))
- window->interlaced_y = 0;
- else
- window->interlaced_y = 1;
-
- if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2))
- window->interlaced_uv = 0;
- else
- window->interlaced_uv = 1;
- }
- else {
- itv->yuv_info.frame_interlaced = 1;
- window->interlaced_y = 1;
- window->interlaced_uv = 1;
- }
- break;
-
- case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */
- default:
- itv->yuv_info.frame_interlaced = 1;
- window->interlaced_y = 1;
- window->interlaced_uv = 1;
- break;
- }
-
/* Sorry, but no negative coords for src */
- if (window->src_x < 0) window->src_x = 0;
- if (window->src_y < 0) window->src_y = 0;
+ if (f->src_x < 0)
+ f->src_x = 0;
+ if (f->src_y < 0)
+ f->src_y = 0;
/* Can only reduce width down to 1/4 original size */
- if ((osd_crop = window->src_w - ( 4 * window->dst_w )) > 0) {
- window->src_x += osd_crop / 2;
- window->src_w = (window->src_w - osd_crop) & ~3;
- window->dst_w = window->src_w / 4;
- window->dst_w += window->dst_w & 1;
+ if ((osd_crop = f->src_w - 4 * f->dst_w) > 0) {
+ f->src_x += osd_crop / 2;
+ f->src_w = (f->src_w - osd_crop) & ~3;
+ f->dst_w = f->src_w / 4;
+ f->dst_w += f->dst_w & 1;
}
/* Can only reduce height down to 1/4 original size */
- if (window->src_h / window->dst_h >= 2) {
- /* Overflow may be because we're running progressive, so force mode switch */
- window->interlaced_y = 1;
+ if (f->src_h / f->dst_h >= 2) {
+ /* Overflow may be because we're running progressive,
+ so force mode switch */
+ f->interlaced_y = 1;
/* Make sure we're still within limits for interlace */
- if ((osd_crop = window->src_h - ( 4 * window->dst_h )) > 0) {
+ if ((osd_crop = f->src_h - 4 * f->dst_h) > 0) {
/* If we reach here we'll have to force the height. */
- window->src_y += osd_crop / 2;
- window->src_h = (window->src_h - osd_crop) & ~3;
- window->dst_h = window->src_h / 4;
- window->dst_h += window->dst_h & 1;
+ f->src_y += osd_crop / 2;
+ f->src_h = (f->src_h - osd_crop) & ~3;
+ f->dst_h = f->src_h / 4;
+ f->dst_h += f->dst_h & 1;
}
}
/* If there's nothing to safe to display, we may as well stop now */
- if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) {
+ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 ||
+ (int)f->src_w <= 2 || (int)f->src_h <= 2) {
return IVTV_YUV_UPDATE_INVALID;
}
/* Ensure video remains inside OSD area */
- osd_scale = (window->src_h << 16) / window->dst_h;
+ osd_scale = (f->src_h << 16) / f->dst_h;
- if ((osd_crop = window->pan_y - window->dst_y) > 0) {
+ if ((osd_crop = f->pan_y - f->dst_y) > 0) {
/* Falls off the upper edge - crop */
- window->src_y += (osd_scale * osd_crop) >> 16;
- window->src_h -= (osd_scale * osd_crop) >> 16;
- window->dst_h -= osd_crop;
- window->dst_y = 0;
- }
- else {
- window->dst_y -= window->pan_y;
+ f->src_y += (osd_scale * osd_crop) >> 16;
+ f->src_h -= (osd_scale * osd_crop) >> 16;
+ f->dst_h -= osd_crop;
+ f->dst_y = 0;
+ } else {
+ f->dst_y -= f->pan_y;
}
- if ((osd_crop = window->dst_h + window->dst_y - window->vis_h) > 0) {
+ if ((osd_crop = f->dst_h + f->dst_y - f->vis_h) > 0) {
/* Falls off the lower edge - crop */
- window->dst_h -= osd_crop;
- window->src_h -= (osd_scale * osd_crop) >> 16;
+ f->dst_h -= osd_crop;
+ f->src_h -= (osd_scale * osd_crop) >> 16;
}
- osd_scale = (window->src_w << 16) / window->dst_w;
+ osd_scale = (f->src_w << 16) / f->dst_w;
- if ((osd_crop = window->pan_x - window->dst_x) > 0) {
+ if ((osd_crop = f->pan_x - f->dst_x) > 0) {
/* Fall off the left edge - crop */
- window->src_x += (osd_scale * osd_crop) >> 16;
- window->src_w -= (osd_scale * osd_crop) >> 16;
- window->dst_w -= osd_crop;
- window->dst_x = 0;
- }
- else {
- window->dst_x -= window->pan_x;
+ f->src_x += (osd_scale * osd_crop) >> 16;
+ f->src_w -= (osd_scale * osd_crop) >> 16;
+ f->dst_w -= osd_crop;
+ f->dst_x = 0;
+ } else {
+ f->dst_x -= f->pan_x;
}
- if ((osd_crop = window->dst_w + window->dst_x - window->vis_w) > 0) {
+ if ((osd_crop = f->dst_w + f->dst_x - f->vis_w) > 0) {
/* Falls off the right edge - crop */
- window->dst_w -= osd_crop;
- window->src_w -= (osd_scale * osd_crop) >> 16;
+ f->dst_w -= osd_crop;
+ f->src_w -= (osd_scale * osd_crop) >> 16;
}
/* The OSD can be moved. Track to it */
- window->dst_x += itv->yuv_info.osd_x_offset;
- window->dst_y += itv->yuv_info.osd_y_offset;
+ f->dst_x += itv->yuv_info.osd_x_offset;
+ f->dst_y += itv->yuv_info.osd_y_offset;
/* Width & height for both src & dst must be even.
Same for coordinates. */
- window->dst_w &= ~1;
- window->dst_x &= ~1;
+ f->dst_w &= ~1;
+ f->dst_x &= ~1;
- window->src_w += window->src_x & 1;
- window->src_x &= ~1;
+ f->src_w += f->src_x & 1;
+ f->src_x &= ~1;
- window->src_w &= ~1;
- window->dst_w &= ~1;
+ f->src_w &= ~1;
+ f->dst_w &= ~1;
- window->dst_h &= ~1;
- window->dst_y &= ~1;
+ f->dst_h &= ~1;
+ f->dst_y &= ~1;
- window->src_h += window->src_y & 1;
- window->src_y &= ~1;
+ f->src_h += f->src_y & 1;
+ f->src_y &= ~1;
- window->src_h &= ~1;
- window->dst_h &= ~1;
+ f->src_h &= ~1;
+ f->dst_h &= ~1;
- /* Due to rounding, we may have reduced the output size to <1/4 of the source
- Check again, but this time just resize. Don't change source coordinates */
- if (window->dst_w < window->src_w / 4) {
- window->src_w &= ~3;
- window->dst_w = window->src_w / 4;
- window->dst_w += window->dst_w & 1;
+ /* Due to rounding, we may have reduced the output size to <1/4 of
+ the source. Check again, but this time just resize. Don't change
+ source coordinates */
+ if (f->dst_w < f->src_w / 4) {
+ f->src_w &= ~3;
+ f->dst_w = f->src_w / 4;
+ f->dst_w += f->dst_w & 1;
}
- if (window->dst_h < window->src_h / 4) {
- window->src_h &= ~3;
- window->dst_h = window->src_h / 4;
- window->dst_h += window->dst_h & 1;
+ if (f->dst_h < f->src_h / 4) {
+ f->src_h &= ~3;
+ f->dst_h = f->src_h / 4;
+ f->dst_h += f->dst_h & 1;
}
/* Check again. If there's nothing to safe to display, stop now */
- if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) {
+ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 ||
+ (int)f->src_w <= 2 || (int)f->src_h <= 2) {
return IVTV_YUV_UPDATE_INVALID;
}
/* Both x offset & width are linked, so they have to be done together */
- if ((itv->yuv_info.old_frame_info.dst_w != window->dst_w) ||
- (itv->yuv_info.old_frame_info.src_w != window->src_w) ||
- (itv->yuv_info.old_frame_info.dst_x != window->dst_x) ||
- (itv->yuv_info.old_frame_info.src_x != window->src_x) ||
- (itv->yuv_info.old_frame_info.pan_x != window->pan_x) ||
- (itv->yuv_info.old_frame_info.vis_w != window->vis_w)) {
+ if ((of->dst_w != f->dst_w) || (of->src_w != f->src_w) ||
+ (of->dst_x != f->dst_x) || (of->src_x != f->src_x) ||
+ (of->pan_x != f->pan_x) || (of->vis_w != f->vis_w)) {
yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL;
}
- if ((itv->yuv_info.old_frame_info.src_h != window->src_h) ||
- (itv->yuv_info.old_frame_info.dst_h != window->dst_h) ||
- (itv->yuv_info.old_frame_info.dst_y != window->dst_y) ||
- (itv->yuv_info.old_frame_info.src_y != window->src_y) ||
- (itv->yuv_info.old_frame_info.pan_y != window->pan_y) ||
- (itv->yuv_info.old_frame_info.vis_h != window->vis_h) ||
- (itv->yuv_info.old_frame_info.lace_mode != window->lace_mode) ||
- (itv->yuv_info.old_frame_info.interlaced_y != window->interlaced_y) ||
- (itv->yuv_info.old_frame_info.interlaced_uv != window->interlaced_uv)) {
+ if ((of->src_h != f->src_h) || (of->dst_h != f->dst_h) ||
+ (of->dst_y != f->dst_y) || (of->src_y != f->src_y) ||
+ (of->pan_y != f->pan_y) || (of->vis_h != f->vis_h) ||
+ (of->lace_mode != f->lace_mode) ||
+ (of->interlaced_y != f->interlaced_y) ||
+ (of->interlaced_uv != f->interlaced_uv)) {
yuv_update |= IVTV_YUV_UPDATE_VERTICAL;
}
@@ -820,24 +782,24 @@ static u32 ivtv_yuv_window_setup (struct ivtv *itv, struct yuv_frame_info *windo
}
/* Update the scaling register to the requested value */
-void ivtv_yuv_work_handler (struct ivtv *itv)
+void ivtv_yuv_work_handler(struct ivtv *itv)
{
- struct yuv_frame_info window;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ struct yuv_frame_info f;
+ int frame = yi->update_frame;
u32 yuv_update;
- int frame = itv->yuv_info.update_frame;
-
-/* IVTV_DEBUG_YUV("Update yuv registers for frame %d\n",frame); */
- memcpy(&window, &itv->yuv_info.new_frame_info[frame], sizeof (window));
+ IVTV_DEBUG_YUV("Update yuv registers for frame %d\n", frame);
+ f = yi->new_frame_info[frame];
/* Update the osd pan info */
- window.pan_x = itv->yuv_info.osd_x_pan;
- window.pan_y = itv->yuv_info.osd_y_pan;
- window.vis_w = itv->yuv_info.osd_vis_w;
- window.vis_h = itv->yuv_info.osd_vis_h;
+ f.pan_x = yi->osd_x_pan;
+ f.pan_y = yi->osd_y_pan;
+ f.vis_w = yi->osd_vis_w;
+ f.vis_h = yi->osd_vis_h;
/* Calculate the display window coordinates. Exit if nothing left */
- if (!(yuv_update = ivtv_yuv_window_setup (itv, &window)))
+ if (!(yuv_update = ivtv_yuv_window_setup(itv, &f)))
return;
if (yuv_update & IVTV_YUV_UPDATE_INVALID) {
@@ -846,16 +808,15 @@ void ivtv_yuv_work_handler (struct ivtv *itv)
write_reg(0x00108080, 0x2898);
if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL)
- ivtv_yuv_handle_horizontal(itv, &window);
+ ivtv_yuv_handle_horizontal(itv, &f);
if (yuv_update & IVTV_YUV_UPDATE_VERTICAL)
- ivtv_yuv_handle_vertical(itv, &window);
+ ivtv_yuv_handle_vertical(itv, &f);
}
-
- memcpy(&itv->yuv_info.old_frame_info, &window, sizeof (itv->yuv_info.old_frame_info));
+ yi->old_frame_info = f;
}
-static void ivtv_yuv_init (struct ivtv *itv)
+static void ivtv_yuv_init(struct ivtv *itv)
{
struct yuv_playback_info *yi = &itv->yuv_info;
@@ -924,25 +885,23 @@ static void ivtv_yuv_init (struct ivtv *itv)
if (!yi->osd_vis_w)
yi->osd_vis_w = 720 - yi->osd_x_offset;
- if (!yi->osd_vis_h)
+ if (!yi->osd_vis_h) {
yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
- else {
+ } else if (yi->osd_vis_h + yi->osd_y_offset > yi->decode_height) {
/* If output video standard has changed, requested height may
- not be legal */
- if (yi->osd_vis_h + yi->osd_y_offset > yi->decode_height) {
- IVTV_DEBUG_WARN("Clipping yuv output - fb size (%d) exceeds video standard limit (%d)\n",
- yi->osd_vis_h + yi->osd_y_offset,
- yi->decode_height);
- yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
- }
+ not be legal */
+ IVTV_DEBUG_WARN("Clipping yuv output - fb size (%d) exceeds video standard limit (%d)\n",
+ yi->osd_vis_h + yi->osd_y_offset,
+ yi->decode_height);
+ yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
}
}
/* 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);
- if (yi->blanking_ptr)
+ yi->blanking_ptr = kzalloc(720 * 16, GFP_KERNEL);
+ if (yi->blanking_ptr) {
yi->blanking_dmaptr = pci_map_single(itv->dev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE);
- else {
+ } else {
yi->blanking_dmaptr = 0;
IVTV_DEBUG_WARN("Failed to allocate yuv blanking buffer\n");
}
@@ -954,77 +913,140 @@ static void ivtv_yuv_init (struct ivtv *itv)
atomic_set(&yi->next_dma_frame, 0);
}
-int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+/* Get next available yuv buffer on PVR350 */
+void ivtv_yuv_next_free(struct ivtv *itv)
{
- DEFINE_WAIT(wait);
- int rc = 0;
- int got_sig = 0;
- int frame, next_fill_frame, last_fill_frame;
- int register_update = 0;
+ int draw, display;
+ struct yuv_playback_info *yi = &itv->yuv_info;
- IVTV_DEBUG_INFO("yuv_prep_frame\n");
+ if (atomic_read(&yi->next_dma_frame) == -1)
+ ivtv_yuv_init(itv);
- if (atomic_read(&itv->yuv_info.next_dma_frame) == -1) ivtv_yuv_init(itv);
+ draw = atomic_read(&yi->next_fill_frame);
+ display = atomic_read(&yi->next_dma_frame);
- frame = atomic_read(&itv->yuv_info.next_fill_frame);
- next_fill_frame = (frame + 1) & 0x3;
- last_fill_frame = (atomic_read(&itv->yuv_info.next_dma_frame)+1) & 0x3;
+ if (display > draw)
+ display -= IVTV_YUV_BUFFERS;
- if (next_fill_frame != last_fill_frame && last_fill_frame != frame) {
- /* Buffers are full - Overwrite the last frame */
- next_fill_frame = frame;
- frame = (frame - 1) & 3;
- register_update = itv->yuv_info.new_frame_info[frame].update;
- }
+ if (draw - display >= yi->max_frames_buffered)
+ draw = (u8)(draw - 1) % IVTV_YUV_BUFFERS;
+ else
+ yi->new_frame_info[draw].update = 0;
+
+ yi->draw_frame = draw;
+}
+
+/* Set up frame according to ivtv_dma_frame parameters */
+void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ u8 frame = yi->draw_frame;
+ u8 last_frame = (u8)(frame - 1) % IVTV_YUV_BUFFERS;
+ struct yuv_frame_info *nf = &yi->new_frame_info[frame];
+ struct yuv_frame_info *of = &yi->new_frame_info[last_frame];
+ int lace_threshold = yi->lace_threshold;
+
+ /* Preserve old update flag in case we're overwriting a queued frame */
+ int update = nf->update;
/* Take a snapshot of the yuv coordinate information */
- itv->yuv_info.new_frame_info[frame].src_x = args->src.left;
- itv->yuv_info.new_frame_info[frame].src_y = args->src.top;
- itv->yuv_info.new_frame_info[frame].src_w = args->src.width;
- itv->yuv_info.new_frame_info[frame].src_h = args->src.height;
- itv->yuv_info.new_frame_info[frame].dst_x = args->dst.left;
- itv->yuv_info.new_frame_info[frame].dst_y = args->dst.top;
- itv->yuv_info.new_frame_info[frame].dst_w = args->dst.width;
- itv->yuv_info.new_frame_info[frame].dst_h = args->dst.height;
- itv->yuv_info.new_frame_info[frame].tru_x = args->dst.left;
- itv->yuv_info.new_frame_info[frame].tru_w = args->src_width;
- itv->yuv_info.new_frame_info[frame].tru_h = args->src_height;
-
- /* Snapshot field order */
- itv->yuv_info.sync_field[frame] = itv->yuv_info.lace_sync_field;
+ nf->src_x = args->src.left;
+ nf->src_y = args->src.top;
+ nf->src_w = args->src.width;
+ nf->src_h = args->src.height;
+ nf->dst_x = args->dst.left;
+ nf->dst_y = args->dst.top;
+ nf->dst_w = args->dst.width;
+ nf->dst_h = args->dst.height;
+ nf->tru_x = args->dst.left;
+ nf->tru_w = args->src_width;
+ nf->tru_h = args->src_height;
/* Are we going to offset the Y plane */
- if (args->src.height + args->src.top < 512-16)
- itv->yuv_info.new_frame_info[frame].offset_y = 1;
- else
- itv->yuv_info.new_frame_info[frame].offset_y = 0;
+ nf->offset_y = (nf->tru_h + nf->src_x < 512 - 16) ? 1 : 0;
/* Snapshot the osd pan info */
- itv->yuv_info.new_frame_info[frame].pan_x = itv->yuv_info.osd_x_pan;
- itv->yuv_info.new_frame_info[frame].pan_y = itv->yuv_info.osd_y_pan;
- itv->yuv_info.new_frame_info[frame].vis_w = itv->yuv_info.osd_vis_w;
- itv->yuv_info.new_frame_info[frame].vis_h = itv->yuv_info.osd_vis_h;
-
- itv->yuv_info.new_frame_info[frame].update = 0;
- itv->yuv_info.new_frame_info[frame].interlaced_y = 0;
- itv->yuv_info.new_frame_info[frame].interlaced_uv = 0;
- itv->yuv_info.new_frame_info[frame].lace_mode = itv->yuv_info.lace_mode;
-
- if (memcmp (&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame],
- sizeof (itv->yuv_info.new_frame_info[frame]))) {
- memcpy(&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], sizeof (itv->yuv_info.old_frame_info_args));
- itv->yuv_info.new_frame_info[frame].update = 1;
-/* IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */
+ nf->pan_x = yi->osd_x_pan;
+ nf->pan_y = yi->osd_y_pan;
+ nf->vis_w = yi->osd_vis_w;
+ nf->vis_h = yi->osd_vis_h;
+
+ nf->update = 0;
+ nf->interlaced_y = 0;
+ nf->interlaced_uv = 0;
+ nf->delay = 0;
+ nf->sync_field = 0;
+ nf->lace_mode = yi->lace_mode & IVTV_YUV_MODE_MASK;
+
+ if (lace_threshold < 0)
+ lace_threshold = yi->decode_height - 1;
+
+ /* Work out the lace settings */
+ switch (nf->lace_mode) {
+ case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */
+ nf->interlaced = 0;
+ if (nf->tru_h < 512 || (nf->tru_h > 576 && nf->tru_h < 1021))
+ nf->interlaced_y = 0;
+ else
+ nf->interlaced_y = 1;
+
+ if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2))
+ nf->interlaced_uv = 0;
+ else
+ nf->interlaced_uv = 1;
+ break;
+
+ case IVTV_YUV_MODE_AUTO:
+ if (nf->tru_h <= lace_threshold || nf->tru_h > 576 || nf->tru_w > 720) {
+ nf->interlaced = 0;
+ if ((nf->tru_h < 512) ||
+ (nf->tru_h > 576 && nf->tru_h < 1021) ||
+ (nf->tru_w > 720 && nf->tru_h < 1021))
+ nf->interlaced_y = 0;
+ else
+ nf->interlaced_y = 1;
+ if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2))
+ nf->interlaced_uv = 0;
+ else
+ nf->interlaced_uv = 1;
+ } else {
+ nf->interlaced = 1;
+ nf->interlaced_y = 1;
+ nf->interlaced_uv = 1;
+ }
+ break;
+
+ case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */
+ default:
+ nf->interlaced = 1;
+ nf->interlaced_y = 1;
+ nf->interlaced_uv = 1;
+ break;
}
- itv->yuv_info.new_frame_info[frame].update |= register_update;
+ if (memcmp(&yi->old_frame_info_args, nf, sizeof(*nf))) {
+ yi->old_frame_info_args = *nf;
+ nf->update = 1;
+ IVTV_DEBUG_YUV("Requesting reg update for frame %d\n", frame);
+ }
- /* Should this frame be delayed ? */
- if (itv->yuv_info.sync_field[frame] != itv->yuv_info.sync_field[(frame - 1) & 3])
- itv->yuv_info.field_delay[frame] = 1;
- else
- itv->yuv_info.field_delay[frame] = 0;
+ nf->update |= update;
+ nf->sync_field = yi->lace_sync_field;
+ nf->delay = nf->sync_field != of->sync_field;
+}
+/* Frame is complete & ready for display */
+void ivtv_yuv_frame_complete(struct ivtv *itv)
+{
+ atomic_set(&itv->yuv_info.next_fill_frame,
+ (itv->yuv_info.draw_frame + 1) % IVTV_YUV_BUFFERS);
+}
+
+int ivtv_yuv_udma_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+ DEFINE_WAIT(wait);
+ int rc = 0;
+ int got_sig = 0;
/* DMA the frame */
mutex_lock(&itv->udma.lock);
@@ -1036,10 +1058,10 @@ int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
ivtv_udma_prepare(itv);
prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
/* if no UDMA is pending and no UDMA is in progress, then the DMA
- is finished */
+ is finished */
while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
/* don't interrupt if the DMA is in progress but break off
- a still pending DMA. */
+ a still pending DMA. */
got_sig = signal_pending(current);
if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
break;
@@ -1057,99 +1079,148 @@ int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
return -EINTR;
}
- atomic_set(&itv->yuv_info.next_fill_frame, next_fill_frame);
+ ivtv_yuv_frame_complete(itv);
mutex_unlock(&itv->udma.lock);
return rc;
}
+/* Setup frame according to V4L2 parameters */
+void ivtv_yuv_setup_stream_frame(struct ivtv *itv)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ struct ivtv_dma_frame dma_args;
+
+ ivtv_yuv_next_free(itv);
+
+ /* Copy V4L2 parameters to an ivtv_dma_frame struct... */
+ dma_args.y_source = 0L;
+ dma_args.uv_source = 0L;
+ dma_args.src.left = 0;
+ dma_args.src.top = 0;
+ dma_args.src.width = yi->v4l2_src_w;
+ dma_args.src.height = yi->v4l2_src_h;
+ dma_args.dst = yi->main_rect;
+ dma_args.src_width = yi->v4l2_src_w;
+ dma_args.src_height = yi->v4l2_src_h;
+
+ /* ... and use the same setup routine as ivtv_yuv_prep_frame */
+ ivtv_yuv_setup_frame(itv, &dma_args);
+
+ if (!itv->dma_data_req_offset)
+ itv->dma_data_req_offset = yuv_offset[yi->draw_frame];
+}
+
+/* Attempt to dma a frame from a user buffer */
+int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void *src)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ struct ivtv_dma_frame dma_args;
+
+ ivtv_yuv_setup_stream_frame(itv);
+
+ /* We only need to supply source addresses for this */
+ dma_args.y_source = src;
+ dma_args.uv_source = src + 720 * ((yi->v4l2_src_h + 31) & ~31);
+ return ivtv_yuv_udma_frame(itv, &dma_args);
+}
+
+/* IVTV_IOC_DMA_FRAME ioctl handler */
+int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+/* IVTV_DEBUG_INFO("yuv_prep_frame\n"); */
+
+ ivtv_yuv_next_free(itv);
+ ivtv_yuv_setup_frame(itv, args);
+ return ivtv_yuv_udma_frame(itv, args);
+}
+
void ivtv_yuv_close(struct ivtv *itv)
{
+ struct yuv_playback_info *yi = &itv->yuv_info;
int h_filter, v_filter_1, v_filter_2;
IVTV_DEBUG_YUV("ivtv_yuv_close\n");
ivtv_waitq(&itv->vsync_waitq);
- atomic_set(&itv->yuv_info.next_dma_frame, -1);
- atomic_set(&itv->yuv_info.next_fill_frame, 0);
+ atomic_set(&yi->next_dma_frame, -1);
+ atomic_set(&yi->next_fill_frame, 0);
/* Reset registers we have changed so mpeg playback works */
/* If we fully restore this register, the display may remain active.
Restore, but set one bit to blank the video. Firmware will always
clear this bit when needed, so not a problem. */
- write_reg(itv->yuv_info.reg_2898 | 0x01000000, 0x2898);
-
- write_reg(itv->yuv_info.reg_2834, 0x02834);
- write_reg(itv->yuv_info.reg_2838, 0x02838);
- write_reg(itv->yuv_info.reg_283c, 0x0283c);
- write_reg(itv->yuv_info.reg_2840, 0x02840);
- write_reg(itv->yuv_info.reg_2844, 0x02844);
- write_reg(itv->yuv_info.reg_2848, 0x02848);
- write_reg(itv->yuv_info.reg_2854, 0x02854);
- write_reg(itv->yuv_info.reg_285c, 0x0285c);
- write_reg(itv->yuv_info.reg_2864, 0x02864);
- write_reg(itv->yuv_info.reg_2870, 0x02870);
- write_reg(itv->yuv_info.reg_2874, 0x02874);
- write_reg(itv->yuv_info.reg_2890, 0x02890);
- write_reg(itv->yuv_info.reg_289c, 0x0289c);
-
- write_reg(itv->yuv_info.reg_2918, 0x02918);
- write_reg(itv->yuv_info.reg_291c, 0x0291c);
- write_reg(itv->yuv_info.reg_2920, 0x02920);
- write_reg(itv->yuv_info.reg_2924, 0x02924);
- write_reg(itv->yuv_info.reg_2928, 0x02928);
- write_reg(itv->yuv_info.reg_292c, 0x0292c);
- write_reg(itv->yuv_info.reg_2930, 0x02930);
- write_reg(itv->yuv_info.reg_2934, 0x02934);
- write_reg(itv->yuv_info.reg_2938, 0x02938);
- write_reg(itv->yuv_info.reg_293c, 0x0293c);
- write_reg(itv->yuv_info.reg_2940, 0x02940);
- write_reg(itv->yuv_info.reg_2944, 0x02944);
- write_reg(itv->yuv_info.reg_2948, 0x02948);
- write_reg(itv->yuv_info.reg_294c, 0x0294c);
- write_reg(itv->yuv_info.reg_2950, 0x02950);
- write_reg(itv->yuv_info.reg_2954, 0x02954);
- write_reg(itv->yuv_info.reg_2958, 0x02958);
- write_reg(itv->yuv_info.reg_295c, 0x0295c);
- write_reg(itv->yuv_info.reg_2960, 0x02960);
- write_reg(itv->yuv_info.reg_2964, 0x02964);
- write_reg(itv->yuv_info.reg_2968, 0x02968);
- write_reg(itv->yuv_info.reg_296c, 0x0296c);
- write_reg(itv->yuv_info.reg_2970, 0x02970);
+ write_reg(yi->reg_2898 | 0x01000000, 0x2898);
+
+ write_reg(yi->reg_2834, 0x02834);
+ write_reg(yi->reg_2838, 0x02838);
+ write_reg(yi->reg_283c, 0x0283c);
+ write_reg(yi->reg_2840, 0x02840);
+ write_reg(yi->reg_2844, 0x02844);
+ write_reg(yi->reg_2848, 0x02848);
+ write_reg(yi->reg_2854, 0x02854);
+ write_reg(yi->reg_285c, 0x0285c);
+ write_reg(yi->reg_2864, 0x02864);
+ write_reg(yi->reg_2870, 0x02870);
+ write_reg(yi->reg_2874, 0x02874);
+ write_reg(yi->reg_2890, 0x02890);
+ write_reg(yi->reg_289c, 0x0289c);
+
+ write_reg(yi->reg_2918, 0x02918);
+ write_reg(yi->reg_291c, 0x0291c);
+ write_reg(yi->reg_2920, 0x02920);
+ write_reg(yi->reg_2924, 0x02924);
+ write_reg(yi->reg_2928, 0x02928);
+ write_reg(yi->reg_292c, 0x0292c);
+ write_reg(yi->reg_2930, 0x02930);
+ write_reg(yi->reg_2934, 0x02934);
+ write_reg(yi->reg_2938, 0x02938);
+ write_reg(yi->reg_293c, 0x0293c);
+ write_reg(yi->reg_2940, 0x02940);
+ write_reg(yi->reg_2944, 0x02944);
+ write_reg(yi->reg_2948, 0x02948);
+ write_reg(yi->reg_294c, 0x0294c);
+ write_reg(yi->reg_2950, 0x02950);
+ write_reg(yi->reg_2954, 0x02954);
+ write_reg(yi->reg_2958, 0x02958);
+ write_reg(yi->reg_295c, 0x0295c);
+ write_reg(yi->reg_2960, 0x02960);
+ write_reg(yi->reg_2964, 0x02964);
+ write_reg(yi->reg_2968, 0x02968);
+ write_reg(yi->reg_296c, 0x0296c);
+ write_reg(yi->reg_2970, 0x02970);
/* Prepare to restore filters */
/* First the horizontal filter */
- if ((itv->yuv_info.reg_2834 & 0x0000FFFF) == (itv->yuv_info.reg_2834 >> 16)) {
+ if ((yi->reg_2834 & 0x0000FFFF) == (yi->reg_2834 >> 16)) {
/* An exact size match uses filter 0 */
h_filter = 0;
- }
- else {
+ } else {
/* Figure out which filter to use */
- h_filter = ((itv->yuv_info.reg_2834 << 16) / (itv->yuv_info.reg_2834 >> 16)) >> 15;
+ h_filter = ((yi->reg_2834 << 16) / (yi->reg_2834 >> 16)) >> 15;
h_filter = (h_filter >> 1) + (h_filter & 1);
/* Only an exact size match can use filter 0. */
- if (h_filter < 1) h_filter = 1;
+ h_filter += !h_filter;
}
/* Now the vertical filter */
- if ((itv->yuv_info.reg_2918 & 0x0000FFFF) == (itv->yuv_info.reg_2918 >> 16)) {
+ if ((yi->reg_2918 & 0x0000FFFF) == (yi->reg_2918 >> 16)) {
/* An exact size match uses filter 0/1 */
v_filter_1 = 0;
v_filter_2 = 1;
- }
- else {
+ } else {
/* Figure out which filter to use */
- v_filter_1 = ((itv->yuv_info.reg_2918 << 16) / (itv->yuv_info.reg_2918 >> 16)) >> 15;
+ v_filter_1 = ((yi->reg_2918 << 16) / (yi->reg_2918 >> 16)) >> 15;
v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
/* Only an exact size match can use filter 0 */
- if (v_filter_1 == 0) v_filter_1 = 1;
+ v_filter_1 += !v_filter_1;
v_filter_2 = v_filter_1;
}
/* Now restore the filters */
- ivtv_yuv_filter (itv,h_filter,v_filter_1,v_filter_2);
+ ivtv_yuv_filter(itv, h_filter, v_filter_1, v_filter_2);
/* and clear a few registers */
write_reg(0, 0x02814);
@@ -1158,19 +1229,18 @@ void ivtv_yuv_close(struct ivtv *itv)
write_reg(0, 0x02910);
/* Release the blanking buffer */
- if (itv->yuv_info.blanking_ptr) {
- kfree (itv->yuv_info.blanking_ptr);
- itv->yuv_info.blanking_ptr = NULL;
- pci_unmap_single(itv->dev, itv->yuv_info.blanking_dmaptr, 720*16, PCI_DMA_TODEVICE);
+ if (yi->blanking_ptr) {
+ kfree(yi->blanking_ptr);
+ yi->blanking_ptr = NULL;
+ pci_unmap_single(itv->dev, yi->blanking_dmaptr, 720*16, PCI_DMA_TODEVICE);
}
/* Invalidate the old dimension information */
- itv->yuv_info.old_frame_info.src_w = 0;
- itv->yuv_info.old_frame_info.src_h = 0;
- itv->yuv_info.old_frame_info_args.src_w = 0;
- itv->yuv_info.old_frame_info_args.src_h = 0;
+ yi->old_frame_info.src_w = 0;
+ yi->old_frame_info.src_h = 0;
+ yi->old_frame_info_args.src_w = 0;
+ yi->old_frame_info_args.src_h = 0;
/* All done. */
clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
}
-
diff --git a/drivers/media/video/ivtv/ivtv-yuv.h b/drivers/media/video/ivtv/ivtv-yuv.h
index 3b966f0a204a..2fe5f1250762 100644
--- a/drivers/media/video/ivtv/ivtv-yuv.h
+++ b/drivers/media/video/ivtv/ivtv-yuv.h
@@ -21,11 +21,6 @@
#ifndef IVTV_YUV_H
#define IVTV_YUV_H
-/* Buffers on hardware offsets */
-#define IVTV_YUV_BUFFER_OFFSET 0x001a8600 /* First YUV Buffer */
-#define IVTV_YUV_BUFFER_OFFSET_1 0x00240400 /* Second YUV Buffer */
-#define IVTV_YUV_BUFFER_OFFSET_2 0x002d8200 /* Third YUV Buffer */
-#define IVTV_YUV_BUFFER_OFFSET_3 0x00370000 /* Fourth YUV Buffer */
#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400 /* Offset to UV Buffer */
/* Offset to filter table in firmware */
@@ -36,11 +31,14 @@
#define IVTV_YUV_UPDATE_VERTICAL 0x02
#define IVTV_YUV_UPDATE_INVALID 0x04
-extern const u32 yuv_offset[4];
+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);
+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);
-void ivtv_yuv_work_handler (struct ivtv *itv);
+void ivtv_yuv_work_handler(struct ivtv *itv);
#endif
diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c
index 52ffd154a3d8..3b23fc05f7c4 100644
--- a/drivers/media/video/ivtv/ivtvfb.c
+++ b/drivers/media/video/ivtv/ivtvfb.c
@@ -504,6 +504,10 @@ static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
ivtvfb_set_display_window(itv, &ivtv_window);
+ /* Pass screen size back to yuv handler */
+ itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride;
+ itv->yuv_info.osd_full_h = ivtv_osd.lines;
+
/* Force update of yuv registers */
itv->yuv_info.yuv_forced_update = 1;
@@ -1053,7 +1057,7 @@ static int ivtvfb_init_card(struct ivtv *itv)
}
itv->osd_info = kzalloc(sizeof(struct osd_info), GFP_ATOMIC);
- if (itv->osd_info == 0) {
+ if (itv->osd_info == NULL) {
IVTVFB_ERR("Failed to allocate memory for osd_info\n");
return -ENOMEM;
}
diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c
index b6cd21e6dab9..4895540be195 100644
--- a/drivers/media/video/ks0127.c
+++ b/drivers/media/video/ks0127.c
@@ -764,7 +764,6 @@ static struct i2c_client ks0127_client_tmpl =
.addr = 0,
.adapter = NULL,
.driver = &i2c_driver_ks0127,
- .usage_count = 0
};
static int ks0127_found_proc(struct i2c_adapter *adapter, int addr, int kind)
diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c
new file mode 100644
index 000000000000..d4bf14c284ef
--- /dev/null
+++ b/drivers/media/video/m52790.c
@@ -0,0 +1,168 @@
+/*
+ * m52790 i2c ivtv driver.
+ * Copyright (C) 2007 Hans Verkuil
+ *
+ * A/V source switching Mitsubishi M52790SP/FP
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/videodev.h>
+#include <media/m52790.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv.h>
+
+MODULE_DESCRIPTION("i2c device driver for m52790 A/V switch");
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+
+struct m52790_state {
+ u16 input;
+ u16 output;
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int m52790_write(struct i2c_client *client)
+{
+ struct m52790_state *state = i2c_get_clientdata(client);
+ u8 sw1 = (state->input | state->output) & 0xff;
+ u8 sw2 = (state->input | state->output) >> 8;
+
+ return i2c_smbus_write_byte_data(client, sw1, sw2);
+}
+
+static int m52790_command(struct i2c_client *client, unsigned int cmd,
+ void *arg)
+{
+ struct m52790_state *state = i2c_get_clientdata(client);
+ struct v4l2_routing *route = arg;
+
+ /* Note: audio and video are linked and cannot be switched separately.
+ So audio and video routing commands are identical for this chip.
+ In theory the video amplifier and audio modes could be handled
+ separately for the output, but that seems to be overkill right now.
+ The same holds for implementing an audio mute control, this is now
+ part of the audio output routing. The normal case is that another
+ chip takes care of the actual muting so making it part of the
+ output routing seems to be the right thing to do for now. */
+ switch (cmd) {
+ case VIDIOC_INT_G_AUDIO_ROUTING:
+ case VIDIOC_INT_G_VIDEO_ROUTING:
+ route->input = state->input;
+ route->output = state->output;
+ break;
+
+ case VIDIOC_INT_S_AUDIO_ROUTING:
+ case VIDIOC_INT_S_VIDEO_ROUTING:
+ state->input = route->input;
+ state->output = route->output;
+ m52790_write(client);
+ break;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_G_REGISTER:
+ case VIDIOC_DBG_S_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (reg->reg != 0)
+ return -EINVAL;
+ if (cmd == VIDIOC_DBG_G_REGISTER)
+ reg->val = state->input | state->output;
+ else {
+ state->input = reg->val & 0x0303;
+ state->output = reg->val & ~0x0303;
+ m52790_write(client);
+ }
+ break;
+ }
+#endif
+
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client, arg,
+ V4L2_IDENT_M52790, 0);
+
+ case VIDIOC_LOG_STATUS:
+ v4l_info(client, "Switch 1: %02x\n",
+ (state->input | state->output) & 0xff);
+ v4l_info(client, "Switch 2: %02x\n",
+ (state->input | state->output) >> 8);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* i2c implementation */
+
+static int m52790_probe(struct i2c_client *client)
+{
+ struct m52790_state *state;
+
+ /* 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, "m52790");
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kmalloc(sizeof(struct m52790_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ state->input = M52790_IN_TUNER;
+ state->output = M52790_OUT_STEREO;
+ i2c_set_clientdata(client, state);
+ m52790_write(client);
+ return 0;
+}
+
+static int m52790_remove(struct i2c_client *client)
+{
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "m52790",
+ .driverid = I2C_DRIVERID_M52790,
+ .command = m52790_command,
+ .probe = m52790_probe,
+ .remove = m52790_remove,
+};
+
diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c
index c31163290432..3d51fa0a52b6 100644
--- a/drivers/media/video/meye.c
+++ b/drivers/media/video/meye.c
@@ -2023,7 +2023,7 @@ static int __init meye_init(void)
if (gbufsize < 0 || gbufsize > MEYE_MAX_BUFSIZE)
gbufsize = MEYE_MAX_BUFSIZE;
gbufsize = PAGE_ALIGN(gbufsize);
- printk(KERN_INFO "meye: using %d buffers with %dk (%dk total)"
+ printk(KERN_INFO "meye: using %d buffers with %dk (%dk total) "
"for capture\n",
gbuffers,
gbufsize / 1024, gbuffers * gbufsize / 1024);
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
index c0c87e06259b..7a11f3159e32 100644
--- a/drivers/media/video/msp3400-driver.c
+++ b/drivers/media/video/msp3400-driver.c
@@ -42,7 +42,8 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
*/
@@ -53,6 +54,7 @@
#include <linux/videodev.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include <media/tvaudio.h>
#include <media/msp3400.h>
#include <linux/kthread.h>
@@ -71,7 +73,8 @@ int msp_debug; /* msp_debug output */
int msp_once; /* no continous stereo monitoring */
int msp_amsound; /* hard-wire AM sound at 6.5 Hz (france),
the autoscan seems work well only with FM... */
-int msp_standard = 1; /* Override auto detect of audio msp_standard, if needed. */
+int msp_standard = 1; /* Override auto detect of audio msp_standard,
+ if needed. */
int msp_dolby;
int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual
@@ -81,12 +84,12 @@ int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual
module_param(opmode, int, 0444);
/* read-write */
-module_param_named(once,msp_once, bool, 0644);
-module_param_named(debug,msp_debug, int, 0644);
-module_param_named(stereo_threshold,msp_stereo_thresh, int, 0644);
-module_param_named(standard,msp_standard, int, 0644);
-module_param_named(amsound,msp_amsound, bool, 0644);
-module_param_named(dolby,msp_dolby, bool, 0644);
+module_param_named(once, msp_once, bool, 0644);
+module_param_named(debug, msp_debug, int, 0644);
+module_param_named(stereo_threshold, msp_stereo_thresh, int, 0644);
+module_param_named(standard, msp_standard, int, 0644);
+module_param_named(amsound, msp_amsound, bool, 0644);
+module_param_named(dolby, msp_dolby, bool, 0644);
MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect");
MODULE_PARM_DESC(once, "No continuous stereo monitoring");
@@ -160,12 +163,13 @@ static int msp_read(struct i2c_client *client, int dev, int addr)
schedule_timeout_interruptible(msecs_to_jiffies(10));
}
if (err == 3) {
- v4l_warn(client, "giving up, resetting chip. Sound will go off, sorry folks :-|\n");
+ v4l_warn(client, "resetting chip, sound will go off.\n");
msp_reset(client);
return -1;
}
retval = read[0] << 8 | read[1];
- v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n", dev, addr, retval);
+ v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n",
+ dev, addr, retval);
return retval;
}
@@ -190,7 +194,8 @@ static int msp_write(struct i2c_client *client, int dev, int addr, int val)
buffer[3] = val >> 8;
buffer[4] = val & 0xff;
- v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n", dev, addr, val);
+ v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n",
+ dev, addr, val);
for (err = 0; err < 3; err++) {
if (i2c_master_send(client, buffer, 5) == 5)
break;
@@ -199,7 +204,7 @@ static int msp_write(struct i2c_client *client, int dev, int addr, int val)
schedule_timeout_interruptible(msecs_to_jiffies(10));
}
if (err == 3) {
- v4l_warn(client, "giving up, resetting chip. Sound will go off, sorry folks :-|\n");
+ v4l_warn(client, "resetting chip, sound will go off.\n");
msp_reset(client);
return -1;
}
@@ -273,7 +278,7 @@ void msp_set_scart(struct i2c_client *client, int in, int out)
state->acb = 0xf60; /* Mute Input and SCART 1 Output */
v4l_dbg(1, msp_debug, client, "scart switch: %s => %d (ACB=0x%04x)\n",
- scart_names[in], out, state->acb);
+ scart_names[in], out, state->acb);
msp_write_dsp(client, 0x13, state->acb);
/* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */
@@ -292,7 +297,8 @@ void msp_set_audio(struct i2c_client *client)
val = (state->volume * 0x7f / 65535) << 8;
v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
- state->muted ? "on" : "off", state->scan_in_progress ? "yes" : "no",
+ state->muted ? "on" : "off",
+ state->scan_in_progress ? "yes" : "no",
state->volume);
msp_write_dsp(client, 0x0000, val);
@@ -681,14 +687,14 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", *a);
switch (*a) {
- case 1024000:
- state->i2s_mode = 0;
- break;
- case 2048000:
- state->i2s_mode = 1;
- break;
- default:
- return -EINVAL;
+ case 1024000:
+ state->i2s_mode = 0;
+ break;
+ case 2048000:
+ state->i2s_mode = 1;
+ break;
+ default:
+ return -EINVAL;
}
break;
}
@@ -698,22 +704,22 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
struct v4l2_queryctrl *qc = arg;
switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill_std(qc);
- default:
- break;
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_MUTE:
+ return v4l2_ctrl_query_fill_std(qc);
+ default:
+ break;
}
if (!state->has_sound_processing)
return -EINVAL;
switch (qc->id) {
- case V4L2_CID_AUDIO_LOUDNESS:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- return v4l2_ctrl_query_fill_std(qc);
- default:
- return -EINVAL;
+ case V4L2_CID_AUDIO_LOUDNESS:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ return v4l2_ctrl_query_fill_std(qc);
+ default:
+ return -EINVAL;
}
}
@@ -735,13 +741,14 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
state->volume, state->muted ? " (muted)" : "");
if (state->has_sound_processing) {
v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n",
- state->balance, state->bass, state->treble,
+ state->balance, state->bass,
+ state->treble,
state->loudness ? "on" : "off");
}
switch (state->mode) {
case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
- case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono + FM-stereo"; break;
+ case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono/stereo"; break;
case MSP_MODE_FM_SAT: p = "Satellite FM-mono"; break;
case MSP_MODE_FM_NICAM1: p = "NICAM/FM (B/G, D/K)"; break;
case MSP_MODE_FM_NICAM2: p = "NICAM/FM (I)"; break;
@@ -772,7 +779,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
}
case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->ident, (state->rev1 << 16) | state->rev2);
+ return v4l2_chip_ident_i2c_client(client, arg, state->ident,
+ (state->rev1 << 16) | state->rev2);
default:
/* unknown */
@@ -783,7 +791,6 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
static int msp_suspend(struct i2c_client *client, pm_message_t state)
{
-
v4l_dbg(1, msp_debug, client, "suspend\n");
msp_reset(client);
return 0;
@@ -791,7 +798,6 @@ static int msp_suspend(struct i2c_client *client, pm_message_t state)
static int msp_resume(struct i2c_client *client)
{
-
v4l_dbg(1, msp_debug, client, "resume\n");
msp_wake_thread(client);
return 0;
@@ -799,11 +805,8 @@ static int msp_resume(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static struct i2c_driver i2c_driver;
-
-static int msp_attach(struct i2c_adapter *adapter, int address, int kind)
+static int msp_probe(struct i2c_client *client)
{
- struct i2c_client *client;
struct msp_state *state;
int (*thread_func)(void *data) = NULL;
int msp_hard;
@@ -812,26 +815,16 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind)
int msp_product, msp_prod_hi, msp_prod_lo;
int msp_rom;
- client = kzalloc(sizeof(*client), GFP_KERNEL);
- if (!client)
- return -ENOMEM;
-
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver;
snprintf(client->name, sizeof(client->name) - 1, "msp3400");
if (msp_reset(client) == -1) {
v4l_dbg(1, msp_debug, client, "msp3400 not found\n");
- kfree(client);
- return 0;
+ return -ENODEV;
}
state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state) {
- kfree(client);
+ if (!state)
return -ENOMEM;
- }
i2c_set_clientdata(client, state);
@@ -853,12 +846,13 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind)
state->rev1 = msp_read_dsp(client, 0x1e);
if (state->rev1 != -1)
state->rev2 = msp_read_dsp(client, 0x1f);
- v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n", state->rev1, state->rev2);
+ v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n",
+ state->rev1, state->rev2);
if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) {
- v4l_dbg(1, msp_debug, client, "not an msp3400 (cannot read chip version)\n");
+ v4l_dbg(1, msp_debug, client,
+ "not an msp3400 (cannot read chip version)\n");
kfree(state);
- kfree(client);
- return 0;
+ return -ENODEV;
}
msp_set_audio(client);
@@ -874,37 +868,55 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind)
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 - '@';
+ state->ident = msp_family * 10000 + 4000 + msp_product * 10 +
+ msp_revision - '@';
/* Has NICAM support: all mspx41x and mspx45x products have NICAM */
- state->has_nicam = msp_prod_hi == 1 || msp_prod_hi == 5;
+ state->has_nicam =
+ msp_prod_hi == 1 || msp_prod_hi == 5;
/* Has radio support: was added with revision G */
- state->has_radio = msp_revision >= 'G';
+ state->has_radio =
+ msp_revision >= 'G';
/* Has headphones output: not for stripped down products */
- state->has_headphones = msp_prod_lo < 5;
+ state->has_headphones =
+ msp_prod_lo < 5;
/* Has scart2 input: not in stripped down products of the '3' family */
- state->has_scart2 = msp_family >= 4 || msp_prod_lo < 7;
+ state->has_scart2 =
+ msp_family >= 4 || msp_prod_lo < 7;
/* Has scart3 input: not in stripped down products of the '3' family */
- state->has_scart3 = msp_family >= 4 || msp_prod_lo < 5;
+ state->has_scart3 =
+ msp_family >= 4 || msp_prod_lo < 5;
/* Has scart4 input: not in pre D revisions, not in stripped D revs */
- state->has_scart4 = msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5);
- /* Has scart2 output: not in stripped down products of the '3' family */
- state->has_scart2_out = msp_family >= 4 || msp_prod_lo < 5;
+ state->has_scart4 =
+ msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5);
+ /* Has scart2 output: not in stripped down products of
+ * the '3' family */
+ state->has_scart2_out =
+ msp_family >= 4 || msp_prod_lo < 5;
/* Has scart2 a volume control? Not in pre-D revisions. */
- state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart2_out;
+ state->has_scart2_out_volume =
+ msp_revision > 'C' && state->has_scart2_out;
/* Has a configurable i2s out? */
- state->has_i2s_conf = msp_revision >= 'G' && msp_prod_lo < 7;
- /* Has subwoofer output: not in pre-D revs and not in stripped down products */
- state->has_subwoofer = msp_revision >= 'D' && msp_prod_lo < 5;
- /* Has soundprocessing (bass/treble/balance/loudness/equalizer): not in
- stripped down products */
- state->has_sound_processing = msp_prod_lo < 7;
+ state->has_i2s_conf =
+ msp_revision >= 'G' && msp_prod_lo < 7;
+ /* Has subwoofer output: not in pre-D revs and not in stripped down
+ * products */
+ state->has_subwoofer =
+ msp_revision >= 'D' && msp_prod_lo < 5;
+ /* Has soundprocessing (bass/treble/balance/loudness/equalizer):
+ * not in stripped down products */
+ state->has_sound_processing =
+ msp_prod_lo < 7;
/* Has Virtual Dolby Surround: only in msp34x1 */
- state->has_virtual_dolby_surround = msp_revision == 'G' && msp_prod_lo == 1;
+ state->has_virtual_dolby_surround =
+ msp_revision == 'G' && msp_prod_lo == 1;
/* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */
- state->has_dolby_pro_logic = msp_revision == 'G' && msp_prod_lo == 2;
- /* The msp343xG supports BTSC only and cannot do Automatic Standard Detection. */
- state->force_btsc = msp_family == 3 && msp_revision == 'G' && msp_prod_hi == 3;
+ state->has_dolby_pro_logic =
+ msp_revision == 'G' && msp_prod_lo == 2;
+ /* The msp343xG supports BTSC only and cannot do Automatic Standard
+ * Detection. */
+ state->force_btsc =
+ msp_family == 3 && msp_revision == 'G' && msp_prod_hi == 3;
state->opmode = opmode;
if (state->opmode == OPMODE_AUTO) {
@@ -919,32 +931,33 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind)
}
/* hello world :-) */
- v4l_info(client, "%s found @ 0x%x (%s)\n", client->name, address << 1, adapter->name);
+ v4l_info(client, "%s found @ 0x%x (%s)\n", client->name,
+ client->addr << 1, client->adapter->name);
v4l_info(client, "%s ", client->name);
if (state->has_nicam && state->has_radio)
- printk("supports nicam and radio, ");
+ printk(KERN_CONT "supports nicam and radio, ");
else if (state->has_nicam)
- printk("supports nicam, ");
+ printk(KERN_CONT "supports nicam, ");
else if (state->has_radio)
- printk("supports radio, ");
- printk("mode is ");
+ printk(KERN_CONT "supports radio, ");
+ printk(KERN_CONT "mode is ");
/* version-specific initialization */
switch (state->opmode) {
case OPMODE_MANUAL:
- printk("manual");
+ printk(KERN_CONT "manual");
thread_func = msp3400c_thread;
break;
case OPMODE_AUTODETECT:
- printk("autodetect");
+ printk(KERN_CONT "autodetect");
thread_func = msp3410d_thread;
break;
case OPMODE_AUTOSELECT:
- printk("autodetect and autoselect");
+ printk(KERN_CONT "autodetect and autoselect");
thread_func = msp34xxg_thread;
break;
}
- printk("\n");
+ printk(KERN_CONT "\n");
/* startup control thread if needed */
if (thread_func) {
@@ -954,24 +967,12 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind)
v4l_warn(client, "kernel_thread() failed\n");
msp_wake_thread(client);
}
-
- /* done */
- i2c_attach_client(client);
-
return 0;
}
-static int msp_probe(struct i2c_adapter *adapter)
-{
- if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, msp_attach);
- return 0;
-}
-
-static int msp_detach(struct i2c_client *client)
+static int msp_remove(struct i2c_client *client)
{
struct msp_state *state = i2c_get_clientdata(client);
- int err;
/* shutdown control thread */
if (state->kthread) {
@@ -980,43 +981,22 @@ static int msp_detach(struct i2c_client *client)
}
msp_reset(client);
- err = i2c_detach_client(client);
- if (err) {
- return err;
- }
-
kfree(state);
- kfree(client);
return 0;
}
/* ----------------------------------------------------------------------- */
-/* i2c implementation */
-static struct i2c_driver i2c_driver = {
- .id = I2C_DRIVERID_MSP3400,
- .attach_adapter = msp_probe,
- .detach_client = msp_detach,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "msp3400",
+ .driverid = I2C_DRIVERID_MSP3400,
+ .command = msp_command,
+ .probe = msp_probe,
+ .remove = msp_remove,
.suspend = msp_suspend,
- .resume = msp_resume,
- .command = msp_command,
- .driver = {
- .name = "msp3400",
- },
+ .resume = msp_resume,
};
-static int __init msp3400_init_module(void)
-{
- return i2c_add_driver(&i2c_driver);
-}
-
-static void __exit msp3400_cleanup_module(void)
-{
- i2c_del_driver(&i2c_driver);
-}
-
-module_init(msp3400_init_module);
-module_exit(msp3400_cleanup_module);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c
index d5ee2629121e..61ec794a737e 100644
--- a/drivers/media/video/msp3400-kthreads.c
+++ b/drivers/media/video/msp3400-kthreads.c
@@ -15,7 +15,8 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
*/
@@ -78,37 +79,37 @@ static struct msp3400c_init_data_dem {
{75, 19, 36, 35, 39, 40},
MSP_CARRIER(5.5), MSP_CARRIER(5.5),
0x00d0, 0x0500, 0x0020, 0x3000
- },{ /* AM (for carrier detect / msp3410) */
+ }, { /* AM (for carrier detect / msp3410) */
{-1, -1, -8, 2, 59, 126},
{-1, -1, -8, 2, 59, 126},
MSP_CARRIER(5.5), MSP_CARRIER(5.5),
0x00d0, 0x0100, 0x0020, 0x3000
- },{ /* FM Radio */
+ }, { /* FM Radio */
{-8, -8, 4, 6, 78, 107},
{-8, -8, 4, 6, 78, 107},
MSP_CARRIER(10.7), MSP_CARRIER(10.7),
0x00d0, 0x0480, 0x0020, 0x3000
- },{ /* Terrestial FM-mono + FM-stereo */
+ }, { /* Terrestial FM-mono + FM-stereo */
{3, 18, 27, 48, 66, 72},
{3, 18, 27, 48, 66, 72},
MSP_CARRIER(5.5), MSP_CARRIER(5.5),
0x00d0, 0x0480, 0x0030, 0x3000
- },{ /* Sat FM-mono */
+ }, { /* Sat FM-mono */
{ 1, 9, 14, 24, 33, 37},
{ 3, 18, 27, 48, 66, 72},
MSP_CARRIER(6.5), MSP_CARRIER(6.5),
0x00c6, 0x0480, 0x0000, 0x3000
- },{ /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */
+ }, { /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */
{-2, -8, -10, 10, 50, 86},
{3, 18, 27, 48, 66, 72},
MSP_CARRIER(5.5), MSP_CARRIER(5.5),
0x00d0, 0x0040, 0x0120, 0x3000
- },{ /* NICAM/FM -- I (6.0/6.552) */
+ }, { /* NICAM/FM -- I (6.0/6.552) */
{2, 4, -6, -4, 40, 94},
{3, 18, 27, 48, 66, 72},
MSP_CARRIER(6.0), MSP_CARRIER(6.0),
0x00d0, 0x0040, 0x0120, 0x3000
- },{ /* NICAM/AM -- L (6.5/5.85) */
+ }, { /* NICAM/AM -- L (6.5/5.85) */
{-2, -8, -10, 10, 50, 86},
{-4, -12, -9, 23, 79, 126},
MSP_CARRIER(6.5), MSP_CARRIER(6.5),
@@ -224,7 +225,9 @@ void msp3400c_set_mode(struct i2c_client *client, int mode)
nor do they support stereo BTSC. */
static void msp3400c_set_audmode(struct i2c_client *client)
{
- static char *strmode[] = { "mono", "stereo", "lang2", "lang1", "lang1+lang2" };
+ static char *strmode[] = {
+ "mono", "stereo", "lang2", "lang1", "lang1+lang2"
+ };
struct msp_state *state = i2c_get_clientdata(client);
char *modestr = (state->audmode >= 0 && state->audmode < 5) ?
strmode[state->audmode] : "unknown";
@@ -298,19 +301,23 @@ static void msp3400c_set_audmode(struct i2c_client *client)
case MSP_MODE_FM_NICAM1:
case MSP_MODE_FM_NICAM2:
case MSP_MODE_AM_NICAM:
- v4l_dbg(1, msp_debug, client, "NICAM set_audmode: %s\n",modestr);
+ v4l_dbg(1, msp_debug, client,
+ "NICAM set_audmode: %s\n", modestr);
if (state->nicam_on)
src = 0x0100; /* NICAM */
break;
case MSP_MODE_BTSC:
- v4l_dbg(1, msp_debug, client, "BTSC set_audmode: %s\n",modestr);
+ v4l_dbg(1, msp_debug, client,
+ "BTSC set_audmode: %s\n", modestr);
break;
case MSP_MODE_EXTERN:
- v4l_dbg(1, msp_debug, client, "extern set_audmode: %s\n",modestr);
+ v4l_dbg(1, msp_debug, client,
+ "extern set_audmode: %s\n", modestr);
src = 0x0200; /* SCART */
break;
case MSP_MODE_FM_RADIO:
- v4l_dbg(1, msp_debug, client, "FM-Radio set_audmode: %s\n",modestr);
+ v4l_dbg(1, msp_debug, client,
+ "FM-Radio set_audmode: %s\n", modestr);
break;
default:
v4l_dbg(1, msp_debug, client, "mono set_audmode\n");
@@ -342,7 +349,8 @@ static void msp3400c_set_audmode(struct i2c_client *client)
src |= 0x0010;
break;
}
- v4l_dbg(1, msp_debug, client, "set_audmode final source/matrix = 0x%x\n", src);
+ v4l_dbg(1, msp_debug, client,
+ "set_audmode final source/matrix = 0x%x\n", src);
msp_set_source(client, src);
}
@@ -351,22 +359,26 @@ static void msp3400c_print_mode(struct i2c_client *client)
{
struct msp_state *state = i2c_get_clientdata(client);
- if (state->main == state->second) {
- v4l_dbg(1, msp_debug, client, "mono sound carrier: %d.%03d MHz\n",
- state->main / 910000, (state->main / 910) % 1000);
- } else {
- v4l_dbg(1, msp_debug, client, "main sound carrier: %d.%03d MHz\n",
- state->main / 910000, (state->main / 910) % 1000);
- }
+ if (state->main == state->second)
+ v4l_dbg(1, msp_debug, client,
+ "mono sound carrier: %d.%03d MHz\n",
+ state->main / 910000, (state->main / 910) % 1000);
+ else
+ v4l_dbg(1, msp_debug, client,
+ "main sound carrier: %d.%03d MHz\n",
+ state->main / 910000, (state->main / 910) % 1000);
if (state->mode == MSP_MODE_FM_NICAM1 || state->mode == MSP_MODE_FM_NICAM2)
- v4l_dbg(1, msp_debug, client, "NICAM/FM carrier : %d.%03d MHz\n",
- state->second / 910000, (state->second/910) % 1000);
+ v4l_dbg(1, msp_debug, client,
+ "NICAM/FM carrier : %d.%03d MHz\n",
+ state->second / 910000, (state->second/910) % 1000);
if (state->mode == MSP_MODE_AM_NICAM)
- v4l_dbg(1, msp_debug, client, "NICAM/AM carrier : %d.%03d MHz\n",
- state->second / 910000, (state->second / 910) % 1000);
+ v4l_dbg(1, msp_debug, client,
+ "NICAM/AM carrier : %d.%03d MHz\n",
+ state->second / 910000, (state->second / 910) % 1000);
if (state->mode == MSP_MODE_FM_TERRA && state->main != state->second) {
- v4l_dbg(1, msp_debug, client, "FM-stereo carrier : %d.%03d MHz\n",
- state->second / 910000, (state->second / 910) % 1000);
+ v4l_dbg(1, msp_debug, client,
+ "FM-stereo carrier : %d.%03d MHz\n",
+ state->second / 910000, (state->second / 910) % 1000);
}
}
@@ -385,7 +397,8 @@ static int msp3400c_detect_stereo(struct i2c_client *client)
val = msp_read_dsp(client, 0x18);
if (val > 32767)
val -= 65536;
- v4l_dbg(2, msp_debug, client, "stereo detect register: %d\n", val);
+ v4l_dbg(2, msp_debug, client,
+ "stereo detect register: %d\n", val);
if (val > 8192) {
rxsubchans = V4L2_TUNER_SUB_STEREO;
} else if (val < -4096) {
@@ -430,7 +443,8 @@ static int msp3400c_detect_stereo(struct i2c_client *client)
}
if (rxsubchans != state->rxsubchans) {
update = 1;
- v4l_dbg(1, msp_debug, client, "watch: rxsubchans %02x => %02x\n",
+ v4l_dbg(1, msp_debug, client,
+ "watch: rxsubchans %02x => %02x\n",
state->rxsubchans, rxsubchans);
state->rxsubchans = rxsubchans;
}
@@ -452,9 +466,8 @@ static void watch_stereo(struct i2c_client *client)
{
struct msp_state *state = i2c_get_clientdata(client);
- if (msp_detect_stereo(client)) {
+ if (msp_detect_stereo(client))
msp_set_audmode(client);
- }
if (msp_once)
state->watch_stereo = 0;
@@ -465,7 +478,7 @@ int msp3400c_thread(void *data)
struct i2c_client *client = data;
struct msp_state *state = i2c_get_clientdata(client);
struct msp3400c_carrier_detect *cd;
- int count, max1, max2, val1, val2, val, this;
+ int count, max1, max2, val1, val2, val, i;
v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n");
@@ -475,7 +488,7 @@ int msp3400c_thread(void *data)
msp_sleep(state, -1);
v4l_dbg(2, msp_debug, client, "msp3400 thread: wakeup\n");
- restart:
+restart:
v4l_dbg(2, msp_debug, client, "thread: restart scan\n");
state->restart = 0;
if (kthread_should_stop())
@@ -483,7 +496,8 @@ int msp3400c_thread(void *data)
if (state->radio || MSP_MODE_EXTERN == state->mode) {
/* no carrier scan, just unmute */
- v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n");
+ v4l_dbg(1, msp_debug, client,
+ "thread: no carrier scan\n");
state->scan_in_progress = 0;
msp_set_audio(client);
continue;
@@ -514,16 +528,17 @@ int msp3400c_thread(void *data)
v4l_dbg(1, msp_debug, client, "AM sound override\n");
}
- for (this = 0; this < count; this++) {
- msp3400c_set_carrier(client, cd[this].cdo, cd[this].cdo);
- if (msp_sleep(state,100))
+ for (i = 0; i < count; i++) {
+ msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo);
+ if (msp_sleep(state, 100))
goto restart;
val = msp_read_dsp(client, 0x1b);
if (val > 32767)
val -= 65536;
if (val1 < val)
- val1 = val, max1 = this;
- v4l_dbg(1, msp_debug, client, "carrier1 val: %5d / %s\n", val,cd[this].name);
+ val1 = val, max1 = i;
+ v4l_dbg(1, msp_debug, client,
+ "carrier1 val: %5d / %s\n", val, cd[i].name);
}
/* carrier detect pass #2 -- second (stereo) carrier */
@@ -550,16 +565,17 @@ int msp3400c_thread(void *data)
count = 0;
max2 = 0;
}
- for (this = 0; this < count; this++) {
- msp3400c_set_carrier(client, cd[this].cdo, cd[this].cdo);
- if (msp_sleep(state,100))
+ for (i = 0; i < count; i++) {
+ msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo);
+ if (msp_sleep(state, 100))
goto restart;
val = msp_read_dsp(client, 0x1b);
if (val > 32767)
val -= 65536;
if (val2 < val)
- val2 = val, max2 = this;
- v4l_dbg(1, msp_debug, client, "carrier2 val: %5d / %s\n", val,cd[this].name);
+ val2 = val, max2 = i;
+ v4l_dbg(1, msp_debug, client,
+ "carrier2 val: %5d / %s\n", val, cd[i].name);
}
/* program the msp3400 according to the results */
@@ -611,7 +627,7 @@ int msp3400c_thread(void *data)
break;
case 0: /* 4.5 */
default:
- no_second:
+no_second:
state->second = msp3400c_carrier_detect_main[max1].cdo;
msp3400c_set_mode(client, MSP_MODE_FM_TERRA);
break;
@@ -632,7 +648,8 @@ int msp3400c_thread(void *data)
while (state->watch_stereo) {
if (msp_sleep(state, count ? 1000 : 5000))
goto restart;
- if (count) count--;
+ if (count)
+ count--;
watch_stereo(client);
}
}
@@ -651,10 +668,10 @@ int msp3410d_thread(void *data)
set_freezable();
for (;;) {
v4l_dbg(2, msp_debug, client, "msp3410 thread: sleep\n");
- msp_sleep(state,-1);
+ msp_sleep(state, -1);
v4l_dbg(2, msp_debug, client, "msp3410 thread: wakeup\n");
- restart:
+restart:
v4l_dbg(2, msp_debug, client, "thread: restart scan\n");
state->restart = 0;
if (kthread_should_stop())
@@ -662,7 +679,8 @@ int msp3410d_thread(void *data)
if (state->mode == MSP_MODE_EXTERN) {
/* no carrier scan needed, just unmute */
- v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n");
+ v4l_dbg(1, msp_debug, client,
+ "thread: no carrier scan\n");
state->scan_in_progress = 0;
msp_set_audio(client);
continue;
@@ -673,7 +691,8 @@ int msp3410d_thread(void *data)
msp_set_audio(client);
/* start autodetect. Note: autodetect is not supported for
- NTSC-M and radio, hence we force the standard in those cases. */
+ NTSC-M and radio, hence we force the standard in those
+ cases. */
if (state->radio)
std = 0x40;
else
@@ -686,8 +705,9 @@ int msp3410d_thread(void *data)
goto restart;
if (msp_debug)
- v4l_dbg(2, msp_debug, client, "setting standard: %s (0x%04x)\n",
- msp_standard_std_name(std), std);
+ v4l_dbg(2, msp_debug, client,
+ "setting standard: %s (0x%04x)\n",
+ msp_standard_std_name(std), std);
if (std != 1) {
/* programmed some specific mode */
@@ -703,7 +723,8 @@ int msp3410d_thread(void *data)
val = msp_read_dem(client, 0x7e);
if (val < 0x07ff)
break;
- v4l_dbg(2, msp_debug, client, "detection still in progress\n");
+ v4l_dbg(2, msp_debug, client,
+ "detection still in progress\n");
}
}
for (i = 0; msp_stdlist[i].name != NULL; i++)
@@ -716,12 +737,13 @@ int msp3410d_thread(void *data)
state->std = val;
state->rxsubchans = V4L2_TUNER_SUB_MONO;
- if (msp_amsound && !state->radio && (state->v4l2_std & V4L2_STD_SECAM) &&
- (val != 0x0009)) {
+ if (msp_amsound && !state->radio &&
+ (state->v4l2_std & V4L2_STD_SECAM) && (val != 0x0009)) {
/* autodetection has failed, let backup */
v4l_dbg(1, msp_debug, client, "autodetection failed,"
" switching to backup standard: %s (0x%04x)\n",
- msp_stdlist[8].name ? msp_stdlist[8].name : "unknown",val);
+ msp_stdlist[8].name ?
+ msp_stdlist[8].name : "unknown", val);
state->std = val = 0x0009;
msp_write_dem(client, 0x20, val);
}
@@ -786,7 +808,8 @@ int msp3410d_thread(void *data)
while (state->watch_stereo) {
if (msp_sleep(state, count ? 1000 : 5000))
goto restart;
- if (count) count--;
+ if (count)
+ count--;
watch_stereo(client);
}
}
@@ -872,8 +895,8 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in)
else
source = (in << 8) | matrix;
- v4l_dbg(1, msp_debug, client, "set source to %d (0x%x) for output %02x\n",
- in, source, reg);
+ v4l_dbg(1, msp_debug, client,
+ "set source to %d (0x%x) for output %02x\n", in, source, reg);
msp_write_dsp(client, reg, source);
}
@@ -948,7 +971,7 @@ int msp34xxg_thread(void *data)
msp_sleep(state, -1);
v4l_dbg(2, msp_debug, client, "msp34xxg thread: wakeup\n");
- restart:
+restart:
v4l_dbg(1, msp_debug, client, "thread: restart scan\n");
state->restart = 0;
if (kthread_should_stop())
@@ -956,7 +979,8 @@ int msp34xxg_thread(void *data)
if (state->mode == MSP_MODE_EXTERN) {
/* no carrier scan needed, just unmute */
- v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n");
+ v4l_dbg(1, msp_debug, client,
+ "thread: no carrier scan\n");
state->scan_in_progress = 0;
msp_set_audio(client);
continue;
@@ -972,7 +996,8 @@ int msp34xxg_thread(void *data)
goto unmute;
/* watch autodetect */
- v4l_dbg(1, msp_debug, client, "started autodetect, waiting for result\n");
+ v4l_dbg(1, msp_debug, client,
+ "started autodetect, waiting for result\n");
for (i = 0; i < 10; i++) {
if (msp_sleep(state, 100))
goto restart;
@@ -983,15 +1008,18 @@ int msp34xxg_thread(void *data)
state->std = val;
break;
}
- v4l_dbg(2, msp_debug, client, "detection still in progress\n");
+ v4l_dbg(2, msp_debug, client,
+ "detection still in progress\n");
}
if (state->std == 1) {
- v4l_dbg(1, msp_debug, client, "detection still in progress after 10 tries. giving up.\n");
+ v4l_dbg(1, msp_debug, client,
+ "detection still in progress after 10 tries. giving up.\n");
continue;
}
- unmute:
- v4l_dbg(1, msp_debug, client, "detected standard: %s (0x%04x)\n",
+unmute:
+ v4l_dbg(1, msp_debug, client,
+ "detected standard: %s (0x%04x)\n",
msp_standard_std_name(state->std), state->std);
if (state->std == 9) {
@@ -1046,9 +1074,11 @@ static int msp34xxg_detect_stereo(struct i2c_client *client)
if (state->std == 0x20)
state->rxsubchans |= V4L2_TUNER_SUB_SAP;
else
- state->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ state->rxsubchans =
+ V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
}
- v4l_dbg(1, msp_debug, client, "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n",
+ v4l_dbg(1, msp_debug, client,
+ "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n",
status, is_stereo, is_bilingual, state->rxsubchans);
return (oldrx != state->rxsubchans);
}
diff --git a/drivers/media/video/mt20xx.c b/drivers/media/video/mt20xx.c
index f49d1f4c40db..b630c26cfe85 100644
--- a/drivers/media/video/mt20xx.c
+++ b/drivers/media/video/mt20xx.c
@@ -14,7 +14,7 @@ static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");
-#define PREFIX "mt20xx "
+#define PREFIX "mt20xx"
/* ---------------------------------------------------------------------- */
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index 98ad3092a079..add6d0d680be 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -149,10 +149,33 @@ struct mxb
static struct saa7146_extension extension;
+static int mxb_check_clients(struct device *dev, void *data)
+{
+ struct mxb* mxb = data;
+ struct i2c_client *client = i2c_verify_client(dev);
+
+ if( !client )
+ return 0;
+
+ if( I2C_ADDR_TEA6420_1 == client->addr )
+ mxb->tea6420_1 = client;
+ if( I2C_ADDR_TEA6420_2 == client->addr )
+ mxb->tea6420_2 = client;
+ if( I2C_TEA6415C_2 == client->addr )
+ mxb->tea6415c = client;
+ if( I2C_ADDR_TDA9840 == client->addr )
+ mxb->tda9840 = client;
+ if( I2C_SAA7111 == client->addr )
+ mxb->saa7111a = client;
+ if( 0x60 == client->addr )
+ mxb->tuner = client;
+
+ return 0;
+}
+
static int mxb_probe(struct saa7146_dev* dev)
{
struct mxb* mxb = NULL;
- struct i2c_client *client;
int result;
if ((result = request_module("saa7111")) < 0) {
@@ -195,20 +218,7 @@ static int mxb_probe(struct saa7146_dev* dev)
}
/* loop through all i2c-devices on the bus and look who is there */
- list_for_each_entry(client, &mxb->i2c_adapter.clients, list) {
- if( I2C_ADDR_TEA6420_1 == client->addr )
- mxb->tea6420_1 = client;
- if( I2C_ADDR_TEA6420_2 == client->addr )
- mxb->tea6420_2 = client;
- if( I2C_TEA6415C_2 == client->addr )
- mxb->tea6415c = client;
- if( I2C_ADDR_TDA9840 == client->addr )
- mxb->tda9840 = client;
- if( I2C_SAA7111 == client->addr )
- mxb->saa7111a = client;
- if( 0x60 == client->addr )
- mxb->tuner = client;
- }
+ device_for_each_child(&mxb->i2c_adapter.dev, mxb, mxb_check_clients);
/* check if all devices are present */
if( 0 == mxb->tea6420_1 || 0 == mxb->tea6420_2 || 0 == mxb->tea6415c
diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig
index d0c2cd785430..6fc1b8be1a1f 100644
--- a/drivers/media/video/pvrusb2/Kconfig
+++ b/drivers/media/video/pvrusb2/Kconfig
@@ -5,6 +5,10 @@ config VIDEO_PVRUSB2
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_CX2341X
+ select VIDEO_SAA711X
+ select VIDEO_CX25840
+ select VIDEO_MSP3400
+ select VIDEO_WM8775
---help---
This is a video4linux driver for Conexant 23416 based
usb2 personal video recorder devices.
@@ -12,32 +16,29 @@ config VIDEO_PVRUSB2
To compile this driver as a module, choose M here: the
module will be called pvrusb2
-config VIDEO_PVRUSB2_29XXX
- bool "Hauppauge WinTV-PVR USB2 support for 29xxx model series"
+config VIDEO_PVRUSB2_ONAIR_CREATOR
+ bool "pvrusb2 driver support for OnAir Creator model"
depends on VIDEO_PVRUSB2 && EXPERIMENTAL
select VIDEO_SAA711X
- select VIDEO_MSP3400
+ select VIDEO_CS53L32A
---help---
- This option enables support for WinTV-PVR USB2 devices whose
- model number is of the form "29xxx" (leading prefix of "29"
- followed by 3 digits).
- To see if you may need this option, examine the white
- sticker on the underside of your device.
+
+ This option enables support for the OnAir Creator USB tuner
+ device. This is a hybrid device, however currently only
+ analog mode is supported.
If you are in doubt, say Y.
-config VIDEO_PVRUSB2_24XXX
- bool "Hauppauge WinTV-PVR USB2 support for 24xxx model series"
+config VIDEO_PVRUSB2_ONAIR_USB2
+ bool "pvrusb2 driver support for OnAir USB2 model"
depends on VIDEO_PVRUSB2 && EXPERIMENTAL
- select VIDEO_CX25840
- select VIDEO_WM8775
+ select VIDEO_SAA711X
+ select VIDEO_CS53L32A
---help---
- This option enables inclusion of additional logic to operate
- newer WinTV-PVR USB2 devices whose model number is of the
- form "24xxx" (leading prefix of "24" followed by 3 digits).
- To see if you may need this option, examine the white
- sticker on the underside of your device. Enabling this
- option will not harm support for older devices.
+
+ This option enables support for the OnAir USB2 tuner device
+ (also known as the Sasem tuner). This is a hybrid device,
+ however currently only analog mode is supported.
If you are in doubt, say Y.
diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile
index 69b3e43cd0eb..47284e558648 100644
--- a/drivers/media/video/pvrusb2/Makefile
+++ b/drivers/media/video/pvrusb2/Makefile
@@ -6,7 +6,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \
pvrusb2-encoder.o pvrusb2-video-v4l.o \
pvrusb2-eeprom.o pvrusb2-tuner.o \
pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \
- pvrusb2-ctrl.o pvrusb2-std.o \
+ pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \
pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \
pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \
$(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y)
diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c
index 379645e481c6..9a7c8e9c3e8b 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-audio.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c
@@ -35,34 +35,58 @@ struct pvr2_msp3400_handler {
};
+
+struct routing_scheme {
+ const int *def;
+ unsigned int cnt;
+};
+
+static const int routing_scheme0[] = {
+ [PVR2_CVAL_INPUT_TV] = MSP_INPUT_DEFAULT,
+ [PVR2_CVAL_INPUT_RADIO] = MSP_INPUT(MSP_IN_SCART2,
+ MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART,
+ MSP_DSP_IN_SCART),
+ [PVR2_CVAL_INPUT_COMPOSITE] = MSP_INPUT(MSP_IN_SCART1,
+ MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART,
+ MSP_DSP_IN_SCART),
+ [PVR2_CVAL_INPUT_SVIDEO] = MSP_INPUT(MSP_IN_SCART1,
+ MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART,
+ MSP_DSP_IN_SCART),
+};
+
+static const struct routing_scheme routing_schemes[] = {
+ [PVR2_ROUTING_SCHEME_HAUPPAUGE] = {
+ .def = routing_scheme0,
+ .cnt = ARRAY_SIZE(routing_scheme0),
+ },
+};
+
/* This function selects the correct audio input source */
static void set_stereo(struct pvr2_msp3400_handler *ctxt)
{
struct pvr2_hdw *hdw = ctxt->hdw;
struct v4l2_routing route;
+ const struct routing_scheme *sp;
+ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo");
- route.input = MSP_INPUT_DEFAULT;
- route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
- switch (hdw->input_val) {
- case PVR2_CVAL_INPUT_TV:
- break;
- case PVR2_CVAL_INPUT_RADIO:
- /* Assume that msp34xx also handle FM decoding, in which case
- we're still using the tuner. */
- /* HV: actually it is more likely to be the SCART2 input if
- the ivtv experience is any indication. */
- route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
- MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
- break;
- case PVR2_CVAL_INPUT_SVIDEO:
- case PVR2_CVAL_INPUT_COMPOSITE:
- /* SCART 1 input */
- route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
- MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
- break;
+ if ((sid < ARRAY_SIZE(routing_schemes)) &&
+ ((sp = routing_schemes + sid) != 0) &&
+ (hdw->input_val >= 0) &&
+ (hdw->input_val < sp->cnt)) {
+ route.input = sp->def[hdw->input_val];
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "*** WARNING *** i2c msp3400 v4l2 set_stereo:"
+ " Invalid routing scheme (%u) and/or input (%d)",
+ sid,hdw->input_val);
+ return;
}
+ route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route);
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/video/pvrusb2/pvrusb2-context.c
index 22719ba861ac..9d94aed2e12d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-context.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-context.c
@@ -31,52 +31,32 @@
static void pvr2_context_destroy(struct pvr2_context *mp)
{
- if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp);
- if (mp->workqueue) {
- flush_workqueue(mp->workqueue);
- destroy_workqueue(mp->workqueue);
- }
+ if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
kfree(mp);
}
-static void pvr2_context_trigger_poll(struct pvr2_context *mp)
-{
- queue_work(mp->workqueue,&mp->workpoll);
-}
-
-
-static void pvr2_context_poll(struct work_struct *work)
-{
- struct pvr2_context *mp =
- container_of(work, struct pvr2_context, workpoll);
- pvr2_context_enter(mp); do {
- pvr2_hdw_poll(mp->hdw);
- } while (0); pvr2_context_exit(mp);
-}
-
-
-static void pvr2_context_setup(struct work_struct *work)
+static void pvr2_context_state_check(struct pvr2_context *mp)
{
- struct pvr2_context *mp =
- container_of(work, struct pvr2_context, workinit);
+ if (mp->init_flag) return;
+
+ switch (pvr2_hdw_get_state(mp->hdw)) {
+ case PVR2_STATE_WARM: break;
+ case PVR2_STATE_ERROR: break;
+ case PVR2_STATE_READY: break;
+ case PVR2_STATE_RUN: break;
+ default: return;
+ }
pvr2_context_enter(mp); do {
- if (!pvr2_hdw_dev_ok(mp->hdw)) break;
- pvr2_hdw_setup(mp->hdw);
- pvr2_hdw_setup_poll_trigger(
- mp->hdw,
- (void (*)(void *))pvr2_context_trigger_poll,
- mp);
- if (!pvr2_hdw_dev_ok(mp->hdw)) break;
- if (!pvr2_hdw_init_ok(mp->hdw)) break;
+ mp->init_flag = !0;
mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw);
if (mp->setup_func) {
mp->setup_func(mp);
}
} while (0); pvr2_context_exit(mp);
-}
+ }
struct pvr2_context *pvr2_context_create(
@@ -96,11 +76,10 @@ struct pvr2_context *pvr2_context_create(
mp = NULL;
goto done;
}
-
- mp->workqueue = create_singlethread_workqueue("pvrusb2");
- INIT_WORK(&mp->workinit, pvr2_context_setup);
- INIT_WORK(&mp->workpoll, pvr2_context_poll);
- queue_work(mp->workqueue,&mp->workinit);
+ pvr2_hdw_set_state_callback(mp->hdw,
+ (void (*)(void *))pvr2_context_state_check,
+ mp);
+ pvr2_context_state_check(mp);
done:
return mp;
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/video/pvrusb2/pvrusb2-context.h
index 6327fa1f7e4f..a04187a93225 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-context.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-context.h
@@ -45,14 +45,11 @@ struct pvr2_context {
struct pvr2_context_stream video_stream;
struct mutex mutex;
int disconnect_flag;
+ int init_flag;
/* Called after pvr2_context initialization is complete */
void (*setup_func)(struct pvr2_context *);
- /* Work queue overhead for out-of-line processing */
- struct workqueue_struct *workqueue;
- struct work_struct workinit;
- struct work_struct workpoll;
};
struct pvr2_channel {
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
index e8a9252c7df6..ffdc45c324e5 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -49,34 +49,89 @@ struct pvr2_v4l_cx2584x {
};
+struct routing_scheme_item {
+ int vid;
+ int aud;
+};
+
+struct routing_scheme {
+ const struct routing_scheme_item *def;
+ unsigned int cnt;
+};
+
+static const struct routing_scheme_item routing_scheme0[] = {
+ [PVR2_CVAL_INPUT_TV] = {
+ .vid = CX25840_COMPOSITE7,
+ .aud = CX25840_AUDIO8,
+ },
+ [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */
+ .vid = CX25840_COMPOSITE3,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_COMPOSITE] = {
+ .vid = CX25840_COMPOSITE3,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_SVIDEO] = {
+ .vid = CX25840_SVIDEO1,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+};
+
+/* Specific to gotview device */
+static const struct routing_scheme_item routing_schemegv[] = {
+ [PVR2_CVAL_INPUT_TV] = {
+ .vid = CX25840_COMPOSITE2,
+ .aud = CX25840_AUDIO5,
+ },
+ [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */
+ .vid = CX25840_COMPOSITE1,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_COMPOSITE] = {
+ .vid = CX25840_COMPOSITE1,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_SVIDEO] = {
+ .vid = (CX25840_SVIDEO_LUMA3|CX25840_SVIDEO_CHROMA4),
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+};
+
+static const struct routing_scheme routing_schemes[] = {
+ [PVR2_ROUTING_SCHEME_HAUPPAUGE] = {
+ .def = routing_scheme0,
+ .cnt = ARRAY_SIZE(routing_scheme0),
+ },
+ [PVR2_ROUTING_SCHEME_GOTVIEW] = {
+ .def = routing_schemegv,
+ .cnt = ARRAY_SIZE(routing_schemegv),
+ },
+};
+
static void set_input(struct pvr2_v4l_cx2584x *ctxt)
{
struct pvr2_hdw *hdw = ctxt->hdw;
struct v4l2_routing route;
enum cx25840_video_input vid_input;
enum cx25840_audio_input aud_input;
+ const struct routing_scheme *sp;
+ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
memset(&route,0,sizeof(route));
- switch(hdw->input_val) {
- case PVR2_CVAL_INPUT_TV:
- vid_input = CX25840_COMPOSITE7;
- aud_input = CX25840_AUDIO8;
- break;
- case PVR2_CVAL_INPUT_RADIO: // Treat same as composite
- case PVR2_CVAL_INPUT_COMPOSITE:
- vid_input = CX25840_COMPOSITE3;
- aud_input = CX25840_AUDIO_SERIAL;
- break;
- case PVR2_CVAL_INPUT_SVIDEO:
- vid_input = CX25840_SVIDEO1;
- aud_input = CX25840_AUDIO_SERIAL;
- break;
- default:
- // Just set it to be composite input for now...
- vid_input = CX25840_COMPOSITE3;
- aud_input = CX25840_AUDIO_SERIAL;
- break;
+ if ((sid < ARRAY_SIZE(routing_schemes)) &&
+ ((sp = routing_schemes + sid) != 0) &&
+ (hdw->input_val >= 0) &&
+ (hdw->input_val < sp->cnt)) {
+ vid_input = sp->def[hdw->input_val].vid;
+ aud_input = sp->def[hdw->input_val].aud;
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "*** WARNING *** i2c cx2584x set_input:"
+ " Invalid routing scheme (%u) and/or input (%d)",
+ sid,hdw->input_val);
+ return;
}
pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input vid=0x%x aud=0x%x",
@@ -140,7 +195,7 @@ static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = {
static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt)
{
ctxt->client->handler = NULL;
- ctxt->hdw->decoder_ctrl = NULL;
+ pvr2_hdw_set_decoder(ctxt->hdw,NULL);
kfree(ctxt);
}
@@ -241,7 +296,7 @@ int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw,
ctxt->client = cp;
ctxt->hdw = hdw;
ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1;
- hdw->decoder_ctrl = &ctxt->ctrl;
+ pvr2_hdw_set_decoder(hdw,&ctxt->ctrl);
cp->handler = &ctxt->handler;
{
/*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h
index da6441b88f31..fca49d8a9311 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debug.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-debug.h
@@ -34,25 +34,26 @@ extern int pvrusb2_debug;
#define PVR2_TRACE_INIT (1 << 5) /* misc initialization steps */
#define PVR2_TRACE_START_STOP (1 << 6) /* Streaming start / stop */
#define PVR2_TRACE_CTL (1 << 7) /* commit of control changes */
-#define PVR2_TRACE_DEBUG (1 << 8) /* Temporary debug code */
-#define PVR2_TRACE_EEPROM (1 << 9) /* eeprom parsing / report */
-#define PVR2_TRACE_STRUCT (1 << 10) /* internal struct creation */
-#define PVR2_TRACE_OPEN_CLOSE (1 << 11) /* application open / close */
-#define PVR2_TRACE_CREG (1 << 12) /* Main critical region entry / exit */
-#define PVR2_TRACE_SYSFS (1 << 13) /* Sysfs driven I/O */
-#define PVR2_TRACE_FIRMWARE (1 << 14) /* firmware upload actions */
-#define PVR2_TRACE_CHIPS (1 << 15) /* chip broadcast operation */
-#define PVR2_TRACE_I2C (1 << 16) /* I2C related stuff */
-#define PVR2_TRACE_I2C_CMD (1 << 17) /* Software commands to I2C modules */
-#define PVR2_TRACE_I2C_CORE (1 << 18) /* I2C core debugging */
-#define PVR2_TRACE_I2C_TRAF (1 << 19) /* I2C traffic through the adapter */
-#define PVR2_TRACE_V4LIOCTL (1 << 20) /* v4l ioctl details */
-#define PVR2_TRACE_ENCODER (1 << 21) /* mpeg2 encoder operation */
-#define PVR2_TRACE_BUF_POOL (1 << 22) /* Track buffer pool management */
-#define PVR2_TRACE_BUF_FLOW (1 << 23) /* Track buffer flow in system */
-#define PVR2_TRACE_DATA_FLOW (1 << 24) /* Track data flow */
-#define PVR2_TRACE_DEBUGIFC (1 << 25) /* Debug interface actions */
-#define PVR2_TRACE_GPIO (1 << 26) /* GPIO state bit changes */
+#define PVR2_TRACE_STATE (1 << 8) /* Device state changes */
+#define PVR2_TRACE_STBITS (1 << 9) /* Individual bit state changes */
+#define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */
+#define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */
+#define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */
+#define PVR2_TRACE_CREG (1 << 13) /* Main critical region entry / exit */
+#define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */
+#define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */
+#define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */
+#define PVR2_TRACE_I2C (1 << 17) /* I2C related stuff */
+#define PVR2_TRACE_I2C_CMD (1 << 18) /* Software commands to I2C modules */
+#define PVR2_TRACE_I2C_CORE (1 << 19) /* I2C core debugging */
+#define PVR2_TRACE_I2C_TRAF (1 << 20) /* I2C traffic through the adapter */
+#define PVR2_TRACE_V4LIOCTL (1 << 21) /* v4l ioctl details */
+#define PVR2_TRACE_ENCODER (1 << 22) /* mpeg2 encoder operation */
+#define PVR2_TRACE_BUF_POOL (1 << 23) /* Track buffer pool management */
+#define PVR2_TRACE_BUF_FLOW (1 << 24) /* Track buffer flow in system */
+#define PVR2_TRACE_DATA_FLOW (1 << 25) /* Track data flow */
+#define PVR2_TRACE_DEBUGIFC (1 << 26) /* Debug interface actions */
+#define PVR2_TRACE_GPIO (1 << 27) /* GPIO state bit changes */
#endif /* __PVRUSB2_HDW_INTERNAL_H */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
index 6f135f4a2497..b0687430fdd4 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
@@ -31,14 +31,6 @@ struct debugifc_mask_item {
unsigned long msk;
};
-static struct debugifc_mask_item mask_items[] = {
- {"ENC_FIRMWARE",(1<<PVR2_SUBSYS_B_ENC_FIRMWARE)},
- {"ENC_CFG",(1<<PVR2_SUBSYS_B_ENC_CFG)},
- {"DIG_RUN",(1<<PVR2_SUBSYS_B_DIGITIZER_RUN)},
- {"USB_RUN",(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)},
- {"ENC_RUN",(1<<PVR2_SUBSYS_B_ENC_RUN)},
-};
-
static unsigned int debugifc_count_whitespace(const char *buf,
unsigned int count)
@@ -148,134 +140,14 @@ static int debugifc_match_keyword(const char *buf,unsigned int count,
}
-static unsigned long debugifc_find_mask(const char *buf,unsigned int count)
-{
- struct debugifc_mask_item *mip;
- unsigned int idx;
- for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) {
- mip = mask_items + idx;
- if (debugifc_match_keyword(buf,count,mip->name)) {
- return mip->msk;
- }
- }
- return 0;
-}
-
-
-static int debugifc_print_mask(char *buf,unsigned int sz,
- unsigned long msk,unsigned long val)
-{
- struct debugifc_mask_item *mip;
- unsigned int idx;
- int bcnt = 0;
- int ccnt;
- for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) {
- mip = mask_items + idx;
- if (!(mip->msk & msk)) continue;
- ccnt = scnprintf(buf,sz,"%s%c%s",
- (bcnt ? " " : ""),
- ((mip->msk & val) ? '+' : '-'),
- mip->name);
- sz -= ccnt;
- buf += ccnt;
- bcnt += ccnt;
- }
- return bcnt;
-}
-
-static unsigned int debugifc_parse_subsys_mask(const char *buf,
- unsigned int count,
- unsigned long *mskPtr,
- unsigned long *valPtr)
-{
- const char *wptr;
- unsigned int consume_cnt = 0;
- unsigned int scnt;
- unsigned int wlen;
- int mode;
- unsigned long m1,msk,val;
-
- msk = 0;
- val = 0;
-
- while (count) {
- scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
- if (!scnt) break;
- consume_cnt += scnt; count -= scnt; buf += scnt;
- if (!wptr) break;
-
- mode = 0;
- if (wlen) switch (wptr[0]) {
- case '+':
- wptr++;
- wlen--;
- break;
- case '-':
- mode = 1;
- wptr++;
- wlen--;
- break;
- }
- if (!wlen) continue;
- m1 = debugifc_find_mask(wptr,wlen);
- if (!m1) break;
- msk |= m1;
- if (!mode) val |= m1;
- }
- *mskPtr = msk;
- *valPtr = val;
- return consume_cnt;
-}
-
-
int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt)
{
int bcnt = 0;
int ccnt;
- struct pvr2_hdw_debug_info dbg;
-
- pvr2_hdw_get_debug_info(hdw,&dbg);
-
- ccnt = scnprintf(buf,acnt,"big lock %s; ctl lock %s",
- (dbg.big_lock_held ? "held" : "free"),
- (dbg.ctl_lock_held ? "held" : "free"));
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- if (dbg.ctl_lock_held) {
- ccnt = scnprintf(buf,acnt,"; cmd_state=%d cmd_code=%d"
- " cmd_wlen=%d cmd_rlen=%d"
- " wpend=%d rpend=%d tmout=%d rstatus=%d"
- " wstatus=%d",
- dbg.cmd_debug_state,dbg.cmd_code,
- dbg.cmd_debug_write_len,
- dbg.cmd_debug_read_len,
- dbg.cmd_debug_write_pend,
- dbg.cmd_debug_read_pend,
- dbg.cmd_debug_timeout,
- dbg.cmd_debug_rstatus,
- dbg.cmd_debug_wstatus);
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- }
- ccnt = scnprintf(buf,acnt,"\n");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(
- buf,acnt,"driver flags: %s %s %s\n",
- (dbg.flag_init_ok ? "initialized" : "uninitialized"),
- (dbg.flag_ok ? "ok" : "fail"),
- (dbg.flag_disconnected ? "disconnected" : "connected"));
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: ");
+ ccnt = scnprintf(buf,acnt,"Driver state info:\n");
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = debugifc_print_mask(buf,acnt,dbg.subsys_flags,dbg.subsys_flags);
+ ccnt = pvr2_hdw_state_report(hdw,buf,acnt);
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"\n");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: ");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = debugifc_print_mask(buf,acnt,~dbg.subsys_flags,dbg.subsys_flags);
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"\n");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-
ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n");
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
ccnt = pvr2_i2c_report(hdw,buf,acnt);
@@ -290,7 +162,6 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw,
{
int bcnt = 0;
int ccnt;
- unsigned long msk;
int ret;
u32 gpio_dir,gpio_in,gpio_out;
@@ -311,28 +182,6 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw,
pvr2_hdw_get_streaming(hdw) ? "on" : "off");
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- msk = pvr2_hdw_subsys_get(hdw);
- ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: ");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = debugifc_print_mask(buf,acnt,msk,msk);
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"\n");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: ");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = debugifc_print_mask(buf,acnt,~msk,msk);
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"\n");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-
- msk = pvr2_hdw_subsys_stream_get(hdw);
- ccnt = scnprintf(buf,acnt,"Subsystems stopped on stream shutdown: ");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = debugifc_print_mask(buf,acnt,msk,msk);
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
- ccnt = scnprintf(buf,acnt,"\n");
- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-
return bcnt;
}
@@ -369,28 +218,10 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf,
return pvr2_upload_firmware2(hdw);
} else if (debugifc_match_keyword(wptr,wlen,"decoder")) {
return pvr2_hdw_cmd_decoder_reset(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"worker")) {
+ return pvr2_hdw_untrip(hdw);
}
return -EINVAL;
- } else if (debugifc_match_keyword(wptr,wlen,"subsys_flags")) {
- unsigned long msk = 0;
- unsigned long val = 0;
- if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) {
- pvr2_trace(PVR2_TRACE_DEBUGIFC,
- "debugifc parse error on subsys mask");
- return -EINVAL;
- }
- pvr2_hdw_subsys_bit_chg(hdw,msk,val);
- return 0;
- } else if (debugifc_match_keyword(wptr,wlen,"stream_flags")) {
- unsigned long msk = 0;
- unsigned long val = 0;
- if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) {
- pvr2_trace(PVR2_TRACE_DEBUGIFC,
- "debugifc parse error on stream mask");
- return -EINVAL;
- }
- pvr2_hdw_subsys_stream_bit_chg(hdw,msk,val);
- return 0;
} else if (debugifc_match_keyword(wptr,wlen,"cpufw")) {
scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
if (!scnt) return -EINVAL;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
new file mode 100644
index 000000000000..4df6d6d936fc
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
@@ -0,0 +1,217 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2007 Mike Isely <isely@pobox.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
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+
+This source file should encompass ALL per-device type information for the
+driver. To define a new device, add elements to the pvr2_device_table and
+pvr2_device_desc structures.
+
+*/
+
+#include "pvrusb2-devattr.h"
+#include <linux/usb.h>
+/* This is needed in order to pull in tuner type ids... */
+#include <linux/i2c.h>
+#include <media/tuner.h>
+
+
+
+/*------------------------------------------------------------------------*/
+/* Hauppauge PVR-USB2 Model 29xxx */
+
+static const char *pvr2_client_29xxx[] = {
+ "msp3400",
+ "saa7115",
+ "tuner",
+};
+
+static const char *pvr2_fw1_names_29xxx[] = {
+ "v4l-pvrusb2-29xxx-01.fw",
+};
+
+static const struct pvr2_device_desc pvr2_device_29xxx = {
+ .description = "WinTV PVR USB2 Model Category 29xxxx",
+ .shortname = "29xxx",
+ .client_modules.lst = pvr2_client_29xxx,
+ .client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_29xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx),
+ .flag_has_hauppauge_rom = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* Hauppauge PVR-USB2 Model 24xxx */
+
+static const char *pvr2_client_24xxx[] = {
+ "cx25840",
+ "tuner",
+ "wm8775",
+};
+
+static const char *pvr2_fw1_names_24xxx[] = {
+ "v4l-pvrusb2-24xxx-01.fw",
+};
+
+static const struct pvr2_device_desc pvr2_device_24xxx = {
+ .description = "WinTV PVR USB2 Model Category 24xxxx",
+ .shortname = "24xxx",
+ .client_modules.lst = pvr2_client_24xxx,
+ .client_modules.cnt = ARRAY_SIZE(pvr2_client_24xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_24xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx),
+ .flag_has_cx25840 = !0,
+ .flag_has_wm8775 = !0,
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_hauppauge_custom_ir = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* GOTVIEW USB2.0 DVD2 */
+
+static const char *pvr2_client_gotview_2[] = {
+ "cx25840",
+ "tuner",
+};
+
+static const struct pvr2_device_desc pvr2_device_gotview_2 = {
+ .description = "Gotview USB 2.0 DVD 2",
+ .shortname = "gv2",
+ .client_modules.lst = pvr2_client_gotview_2,
+ .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2),
+ .flag_has_cx25840 = !0,
+ .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW,
+};
+
+
+
+#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR
+/*------------------------------------------------------------------------*/
+/* OnAir Creator */
+
+static const char *pvr2_client_onair_creator[] = {
+ "saa7115",
+ "tuner",
+ "cs53l32a",
+};
+
+static const struct pvr2_device_desc pvr2_device_onair_creator = {
+ .description = "OnAir Creator Hybrid USB tuner",
+ .shortname = "oac",
+ .client_modules.lst = pvr2_client_onair_creator,
+ .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_creator),
+ .default_tuner_type = TUNER_LG_TDVS_H06XF,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+};
+#endif
+
+
+
+#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_USB2
+/*------------------------------------------------------------------------*/
+/* OnAir USB 2.0 */
+
+static const char *pvr2_client_onair_usb2[] = {
+ "saa7115",
+ "tuner",
+ "cs53l32a",
+};
+
+static const struct pvr2_device_desc pvr2_device_onair_usb2 = {
+ .description = "OnAir USB2 Hybrid USB tuner",
+ .shortname = "oa2",
+ .client_modules.lst = pvr2_client_onair_usb2,
+ .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_usb2),
+ .default_tuner_type = TUNER_PHILIPS_ATSC,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+};
+#endif
+
+
+
+/*------------------------------------------------------------------------*/
+/* Hauppauge PVR-USB2 Model 75xxx */
+
+static const char *pvr2_client_75xxx[] = {
+ "cx25840",
+ "tuner",
+};
+
+static const char *pvr2_fw1_names_75xxx[] = {
+ "v4l-pvrusb2-73xxx-01.fw",
+};
+
+static const struct pvr2_device_desc pvr2_device_75xxx = {
+ .description = "WinTV PVR USB2 Model Category 75xxxx",
+ .shortname = "75xxx",
+ .client_modules.lst = pvr2_client_75xxx,
+ .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_75xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
+ .flag_has_cx25840 = !0,
+ .flag_has_hauppauge_rom = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .default_std_mask = V4L2_STD_NTSC_M,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+
+struct usb_device_id pvr2_device_table[] = {
+ { USB_DEVICE(0x2040, 0x2900),
+ .driver_info = (kernel_ulong_t)&pvr2_device_29xxx},
+ { USB_DEVICE(0x2040, 0x2400),
+ .driver_info = (kernel_ulong_t)&pvr2_device_24xxx},
+ { USB_DEVICE(0x1164, 0x0622),
+ .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2},
+#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR
+ { USB_DEVICE(0x11ba, 0x1003),
+ .driver_info = (kernel_ulong_t)&pvr2_device_onair_creator},
+#endif
+#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_USB2
+ { USB_DEVICE(0x11ba, 0x1001),
+ .driver_info = (kernel_ulong_t)&pvr2_device_onair_usb2},
+#endif
+ { USB_DEVICE(0x2040, 0x7500),
+ .driver_info = (kernel_ulong_t)&pvr2_device_75xxx},
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, pvr2_device_table);
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h
new file mode 100644
index 000000000000..64b467f0637f
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h
@@ -0,0 +1,119 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_DEVATTR_H
+#define __PVRUSB2_DEVATTR_H
+
+#include <linux/mod_devicetable.h>
+#include <linux/videodev2.h>
+
+/*
+
+ This header defines structures used to describe attributes of a device.
+
+*/
+
+
+struct pvr2_string_table {
+ const char **lst;
+ unsigned int cnt;
+};
+
+#define PVR2_ROUTING_SCHEME_HAUPPAUGE 0
+#define PVR2_ROUTING_SCHEME_GOTVIEW 1
+
+/* This describes a particular hardware type (except for the USB device ID
+ which must live in a separate structure due to environmental
+ constraints). See the top of pvrusb2-hdw.c for where this is
+ instantiated. */
+struct pvr2_device_desc {
+ /* Single line text description of hardware */
+ const char *description;
+
+ /* Single token identifier for hardware */
+ const char *shortname;
+
+ /* List of additional client modules we need to load */
+ struct pvr2_string_table client_modules;
+
+ /* List of FX2 firmware file names we should search; if empty then
+ FX2 firmware check / load is skipped and we assume the device
+ was initialized from internal ROM. */
+ struct pvr2_string_table fx2_firmware;
+
+ /* Signal routing scheme used by device, contains one of
+ PVR2_ROUTING_SCHEME_XXX. Schemes have to be defined as we
+ encounter them. This is an arbitrary integer scheme id; its
+ meaning is contained entirely within the driver and is
+ interpreted by logic which must send commands to the chip-level
+ drivers (search for things which touch this field). */
+ unsigned int signal_routing_scheme;
+
+ /* V4L tuner type ID to use with this device (only used if the
+ driver could not discover the type any other way). */
+ int default_tuner_type;
+
+ /* Initial standard bits to use for this device, if not zero.
+ Anything set here is also implied as an available standard.
+ Note: This is ignored if overridden on the module load line via
+ the video_std module option. */
+ v4l2_std_id default_std_mask;
+
+ /* If set, we don't bother trying to load cx23416 firmware. */
+ char flag_skip_cx23416_firmware;
+
+ /* Device has a hauppauge eeprom which we can interrogate. */
+ char flag_has_hauppauge_rom;
+
+ /* Device does not require a powerup command to be issued. */
+ char flag_no_powerup;
+
+ /* Device has a cx25840 - this enables special additional logic to
+ handle it. */
+ char flag_has_cx25840;
+
+ /* Device has a wm8775 - this enables special additional logic to
+ ensure that it is found. */
+ char flag_has_wm8775;
+
+ /* Device has IR hardware that can be faked into looking like a
+ normal Hauppauge i2c IR receiver. This is currently very
+ specific to the 24xxx device, where Hauppauge had replaced their
+ 'standard' I2C IR receiver with a bunch of FPGA logic controlled
+ directly via the FX2. Turning this on tells the pvrusb2 driver
+ to virtualize the presence of the non-existant IR receiver chip and
+ implement the virtual receiver in terms of appropriate FX2
+ commands. */
+ char flag_has_hauppauge_custom_ir;
+};
+
+extern struct usb_device_id pvr2_device_table[];
+
+#endif /* __PVRUSB2_HDW_INTERNAL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
index 45cbca0143ca..5ef005947b04 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
@@ -144,6 +144,7 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw)
trace_eeprom("serial_number=%d",tvdata.serial_number);
trace_eeprom("rev_str=%s",tvdata.rev_str);
hdw->tuner_type = tvdata.tuner_type;
+ hdw->tuner_updated = !0;
hdw->serial_number = tvdata.serial_number;
hdw->std_mask_eeprom = tvdata.tuner_formats;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
index 205087a3e136..64062879981e 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
@@ -140,7 +140,7 @@ static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,
cx2341x.ko to write to our encoder (by handing it a pointer to this
function). For earlier kernels this doesn't really matter. */
static int pvr2_encoder_cmd(void *ctxt,
- int cmd,
+ u32 cmd,
int arg_cnt_send,
int arg_cnt_recv,
u32 *argp)
@@ -209,7 +209,7 @@ static int pvr2_encoder_cmd(void *ctxt,
LOCK_TAKE(hdw->ctl_lock); do {
- if (!hdw->flag_encoder_ok) {
+ if (!hdw->state_encoder_ok) {
ret = -EIO;
break;
}
@@ -278,12 +278,15 @@ static int pvr2_encoder_cmd(void *ctxt,
ret = -EBUSY;
}
if (ret) {
- hdw->flag_encoder_ok = 0;
+ hdw->state_encoder_ok = 0;
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "State bit %s <-- %s",
+ "state_encoder_ok",
+ (hdw->state_encoder_ok ? "true" : "false"));
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Giving up on command."
- " It is likely that"
- " this is a bad idea...");
+ " This is normally recovered by the driver.");
break;
}
wrData[0] = 0x7;
@@ -366,13 +369,13 @@ static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
/* This ENC_MISC(3,encMisc3Arg) command is critical - without
it there will eventually be video corruption. Also, the
- 29xxx case is strange - the Windows driver is passing 1
- regardless of device type but if we have 1 for 29xxx device
- the video turns sluggish. */
- switch (hdw->hdw_type) {
- case PVR2_HDW_TYPE_24XXX: encMisc3Arg = 1; break;
- case PVR2_HDW_TYPE_29XXX: encMisc3Arg = 0; break;
- default: break;
+ saa7115 case is strange - the Windows driver is passing 1
+ regardless of device type but if we have 1 for saa7115
+ devices the video turns sluggish. */
+ if (hdw->hdw_desc->flag_has_cx25840) {
+ encMisc3Arg = 1;
+ } else {
+ encMisc3Arg = 0;
}
ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,
encMisc3Arg,0,0);
@@ -394,6 +397,24 @@ static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
return ret;
}
+int pvr2_encoder_adjust(struct pvr2_hdw *hdw)
+{
+ int ret;
+ ret = cx2341x_update(hdw,pvr2_encoder_cmd,
+ (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
+ &hdw->enc_ctl_state);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Error from cx2341x module code=%d",ret);
+ } else {
+ memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
+ sizeof(struct cx2341x_mpeg_params));
+ hdw->enc_cur_valid = !0;
+ }
+ return ret;
+}
+
+
int pvr2_encoder_configure(struct pvr2_hdw *hdw)
{
int ret;
@@ -412,7 +433,7 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw)
/* saa7115: 0xf0 */
val = 0xf0;
- if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+ if (hdw->hdw_desc->flag_has_cx25840) {
/* ivtv cx25840: 0x140 */
val = 0x140;
}
@@ -436,18 +457,10 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw)
return ret;
}
- ret = cx2341x_update(hdw,pvr2_encoder_cmd,
- (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
- &hdw->enc_ctl_state);
- if (ret) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Error from cx2341x module code=%d",ret);
- return ret;
- }
-
- ret = 0;
+ ret = pvr2_encoder_adjust(hdw);
+ if (ret) return ret;
- if (!ret) ret = pvr2_encoder_vcmd(
+ ret = pvr2_encoder_vcmd(
hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
if (ret) {
@@ -456,10 +469,6 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw)
return ret;
}
- hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
- memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
- sizeof(struct cx2341x_mpeg_params));
- hdw->enc_cur_valid = !0;
return 0;
}
@@ -478,7 +487,7 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw)
pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1,
hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0);
- switch (hdw->config) {
+ switch (hdw->active_stream_type) {
case pvr2_config_vbi:
status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
0x01,0x14);
@@ -492,9 +501,6 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw)
0,0x13);
break;
}
- if (!status) {
- hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN);
- }
return status;
}
@@ -505,7 +511,7 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw)
/* mask all interrupts */
pvr2_write_register(hdw, 0x0048, 0xffffffff);
- switch (hdw->config) {
+ switch (hdw->active_stream_type) {
case pvr2_config_vbi:
status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
0x01,0x01,0x14);
@@ -526,9 +532,6 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw)
pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401);
pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
- if (!status) {
- hdw->subsys_enabled_mask &= ~(1<<PVR2_SUBSYS_B_ENC_RUN);
- }
return status;
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.h b/drivers/media/video/pvrusb2/pvrusb2-encoder.h
index 01b5a0b89c03..54caf2e3c428 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.h
@@ -25,6 +25,7 @@
struct pvr2_hdw;
+int pvr2_encoder_adjust(struct pvr2_hdw *);
int pvr2_encoder_configure(struct pvr2_hdw *);
int pvr2_encoder_start(struct pvr2_hdw *);
int pvr2_encoder_stop(struct pvr2_hdw *);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index f873994b088c..d7a216b41b72 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -35,10 +35,12 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
+#include <linux/workqueue.h>
#include <linux/mutex.h>
#include "pvrusb2-hdw.h"
#include "pvrusb2-io.h"
#include <media/cx2341x.h>
+#include "pvrusb2-devattr.h"
/* Legal values for PVR2_CID_HSM */
#define PVR2_CVAL_HSM_FAIL 0
@@ -161,10 +163,6 @@ struct pvr2_decoder_ctrl {
#define FW1_STATE_RELOAD 3
#define FW1_STATE_OK 4
-/* Known major hardware variants, keyed from device ID */
-#define PVR2_HDW_TYPE_29XXX 0
-#define PVR2_HDW_TYPE_24XXX 1
-
typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16);
#define PVR2_I2C_FUNC_CNT 128
@@ -176,8 +174,15 @@ struct pvr2_hdw {
struct usb_device *usb_dev;
struct usb_interface *usb_intf;
- /* Device type, one of PVR2_HDW_TYPE_xxxxx */
- unsigned int hdw_type;
+ /* Device description, anything that must adjust behavior based on
+ device specific info will use information held here. */
+ const struct pvr2_device_desc *hdw_desc;
+
+ /* Kernel worker thread handling */
+ struct workqueue_struct *workqueue;
+ struct work_struct workpoll; /* Update driver state */
+ struct work_struct worki2csync; /* Update i2c clients */
+ struct work_struct workinit; /* Driver initialization sequence */
/* Video spigot */
struct pvr2_stream *vid_stream;
@@ -186,9 +191,6 @@ struct pvr2_hdw {
struct mutex big_lock_mutex;
int big_lock_held; /* For debugging */
- void (*poll_trigger_func)(void *);
- void *poll_trigger_data;
-
char name[32];
/* I2C stuff */
@@ -215,9 +217,9 @@ struct pvr2_hdw {
struct urb *ctl_read_urb;
unsigned char *ctl_write_buffer;
unsigned char *ctl_read_buffer;
- volatile int ctl_write_pend_flag;
- volatile int ctl_read_pend_flag;
- volatile int ctl_timeout_flag;
+ int ctl_write_pend_flag;
+ int ctl_read_pend_flag;
+ int ctl_timeout_flag;
struct completion ctl_done;
unsigned char cmd_buffer[PVR2_CTL_BUFFSIZE];
int cmd_debug_state; // Low level command debugging info
@@ -225,14 +227,48 @@ struct pvr2_hdw {
unsigned int cmd_debug_write_len; //
unsigned int cmd_debug_read_len; //
+ /* Bits of state that describe what is going on with various parts
+ of the driver. */
+ int state_encoder_ok; /* Encoder is operational */
+ int state_encoder_run; /* Encoder is running */
+ int state_encoder_config; /* Encoder is configured */
+ int state_encoder_waitok; /* Encoder pre-wait done */
+ int state_decoder_run; /* Decoder is running */
+ int state_usbstream_run; /* FX2 is streaming */
+ int state_decoder_quiescent; /* Decoder idle for > 50msec */
+ int state_pipeline_config; /* Pipeline is configured */
+ int state_pipeline_req; /* Somebody wants to stream */
+ int state_pipeline_pause; /* Pipeline must be paused */
+ int state_pipeline_idle; /* Pipeline not running */
+
+ /* This is the master state of the driver. It is the combined
+ result of other bits of state. Examining this will indicate the
+ overall state of the driver. Values here are one of
+ PVR2_STATE_xxxx */
+ unsigned int master_state;
+
+ /* True if states must be re-evaluated */
+ int state_stale;
+
+ void (*state_func)(void *);
+ void *state_data;
+
+ /* Timer for measuring decoder settling time */
+ struct timer_list quiescent_timer;
+
+ /* Timer for measuring encoder pre-wait time */
+ struct timer_list encoder_wait_timer;
+
+ /* Place to block while waiting for state changes */
+ wait_queue_head_t state_wait_data;
+
+
int flag_ok; /* device in known good state */
int flag_disconnected; /* flag_ok == 0 due to disconnect */
int flag_init_ok; /* true if structure is fully initialized */
- int flag_streaming_enabled; /* true if streaming should be on */
int fw1_state; /* current situation with fw1 */
- int flag_encoder_ok; /* True if encoder is healthy */
-
- int flag_decoder_is_tuned;
+ int flag_decoder_missed;/* We've noticed missing decoder */
+ int flag_tripped; /* Indicates overall failure to start */
struct pvr2_decoder_ctrl *decoder_ctrl;
@@ -241,12 +277,6 @@ struct pvr2_hdw {
unsigned int fw_size;
int fw_cpu_flag; /* True if we are dealing with the CPU */
- // Which subsystem pieces have been enabled / configured
- unsigned long subsys_enabled_mask;
-
- // Which subsystems are manipulated to enable streaming
- unsigned long subsys_stream_mask;
-
// True if there is a request to trigger logging of state in each
// module.
int log_requested;
@@ -296,13 +326,16 @@ struct pvr2_hdw {
/* Location of eeprom or a negative number if none */
int eeprom_addr;
- enum pvr2_config config;
+ enum pvr2_config active_stream_type;
+ enum pvr2_config desired_stream_type;
/* Control state needed for cx2341x module */
struct cx2341x_mpeg_params enc_cur_state;
struct cx2341x_mpeg_params enc_ctl_state;
/* True if an encoder attribute has changed */
int enc_stale;
+ /* True if an unsafe encoder attribute has changed */
+ int enc_unsafe_stale;
/* True if enc_cur_state is valid */
int enc_cur_valid;
@@ -332,6 +365,7 @@ struct pvr2_hdw {
/* This function gets the current frequency */
unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *);
+void pvr2_hdw_set_decoder(struct pvr2_hdw *,struct pvr2_decoder_ctrl *);
#endif /* __PVRUSB2_HDW_INTERNAL_H */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 402c59488253..41ae980405ed 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -41,47 +41,6 @@
#define TV_MIN_FREQ 55250000L
#define TV_MAX_FREQ 850000000L
-struct usb_device_id pvr2_device_table[] = {
- [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) },
- [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) },
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, pvr2_device_table);
-
-static const char *pvr2_device_names[] = {
- [PVR2_HDW_TYPE_29XXX] = "WinTV PVR USB2 Model Category 29xxxx",
- [PVR2_HDW_TYPE_24XXX] = "WinTV PVR USB2 Model Category 24xxxx",
-};
-
-struct pvr2_string_table {
- const char **lst;
- unsigned int cnt;
-};
-
-// Names of other client modules to request for 24xxx model hardware
-static const char *pvr2_client_24xxx[] = {
- "cx25840",
- "tuner",
- "wm8775",
-};
-
-// Names of other client modules to request for 29xxx model hardware
-static const char *pvr2_client_29xxx[] = {
- "msp3400",
- "saa7115",
- "tuner",
-};
-
-static struct pvr2_string_table pvr2_client_lists[] = {
- [PVR2_HDW_TYPE_29XXX] = {
- pvr2_client_29xxx, ARRAY_SIZE(pvr2_client_29xxx)
- },
- [PVR2_HDW_TYPE_24XXX] = {
- pvr2_client_24xxx, ARRAY_SIZE(pvr2_client_24xxx)
- },
-};
-
static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};
static DEFINE_MUTEX(pvr2_unit_mtx);
@@ -246,32 +205,46 @@ static const char *control_values_hsm[] = {
};
-static const char *control_values_subsystem[] = {
- [PVR2_SUBSYS_B_ENC_FIRMWARE] = "enc_firmware",
- [PVR2_SUBSYS_B_ENC_CFG] = "enc_config",
- [PVR2_SUBSYS_B_DIGITIZER_RUN] = "digitizer_run",
- [PVR2_SUBSYS_B_USBSTREAM_RUN] = "usbstream_run",
- [PVR2_SUBSYS_B_ENC_RUN] = "enc_run",
+static const char *pvr2_state_names[] = {
+ [PVR2_STATE_NONE] = "none",
+ [PVR2_STATE_DEAD] = "dead",
+ [PVR2_STATE_COLD] = "cold",
+ [PVR2_STATE_WARM] = "warm",
+ [PVR2_STATE_ERROR] = "error",
+ [PVR2_STATE_READY] = "ready",
+ [PVR2_STATE_RUN] = "run",
};
+
+static void pvr2_hdw_state_sched(struct pvr2_hdw *);
+static int pvr2_hdw_state_eval(struct pvr2_hdw *);
static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long);
+static void pvr2_hdw_worker_i2c(struct work_struct *work);
+static void pvr2_hdw_worker_poll(struct work_struct *work);
+static void pvr2_hdw_worker_init(struct work_struct *work);
+static int pvr2_hdw_wait(struct pvr2_hdw *,int state);
+static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *);
+static void pvr2_hdw_state_log_state(struct pvr2_hdw *);
static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl);
-static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw);
+static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw);
static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw);
static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw);
static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw);
-static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw);
-static void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
- unsigned long msk,
- unsigned long val);
-static void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
- unsigned long msk,
- unsigned long val);
+static void pvr2_hdw_quiescent_timeout(unsigned long);
+static void pvr2_hdw_encoder_wait_timeout(unsigned long);
static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
unsigned int timeout,int probe_fl,
void *write_data,unsigned int write_len,
void *read_data,unsigned int read_len);
+
+static void trace_stbit(const char *name,int val)
+{
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "State bit %s <-- %s",
+ name,(val ? "true" : "false"));
+}
+
static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp)
{
struct pvr2_hdw *hdw = cptr->hdw;
@@ -380,8 +353,8 @@ static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp)
{
- /* Actual minimum depends on device type. */
- if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+ /* Actual minimum depends on device digitizer type. */
+ if (cptr->hdw->hdw_desc->flag_has_cx25840) {
*vp = 75;
} else {
*vp = 17;
@@ -480,6 +453,7 @@ static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr)
static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr)
{
cptr->hdw->enc_stale = 0;
+ cptr->hdw->enc_unsafe_stale = 0;
}
static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
@@ -502,6 +476,7 @@ static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
{
int ret;
+ struct pvr2_hdw *hdw = cptr->hdw;
struct v4l2_ext_controls cs;
struct v4l2_ext_control c1;
memset(&cs,0,sizeof(cs));
@@ -510,10 +485,22 @@ static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
cs.count = 1;
c1.id = cptr->info->v4l_id;
c1.value = v;
- ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state, 0, &cs,
+ ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
+ hdw->state_encoder_run, &cs,
VIDIOC_S_EXT_CTRLS);
+ if (ret == -EBUSY) {
+ /* Oops. cx2341x is telling us it's not safe to change
+ this control while we're capturing. Make a note of this
+ fact so that the pipeline will be stopped the next time
+ controls are committed. Then go on ahead and store this
+ change anyway. */
+ ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
+ 0, &cs,
+ VIDIOC_S_EXT_CTRLS);
+ if (!ret) hdw->enc_unsafe_stale = !0;
+ }
if (ret) return ret;
- cptr->hdw->enc_stale = !0;
+ hdw->enc_stale = !0;
return 0;
}
@@ -544,7 +531,13 @@ static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr)
static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp)
{
- *vp = cptr->hdw->flag_streaming_enabled;
+ *vp = cptr->hdw->state_pipeline_req;
+ return 0;
+}
+
+static int ctrl_masterstate_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->master_state;
return 0;
}
@@ -657,29 +650,6 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp)
return 0;
}
-static int ctrl_subsys_get(struct pvr2_ctrl *cptr,int *vp)
-{
- *vp = cptr->hdw->subsys_enabled_mask;
- return 0;
-}
-
-static int ctrl_subsys_set(struct pvr2_ctrl *cptr,int m,int v)
-{
- pvr2_hdw_subsys_bit_chg_no_lock(cptr->hdw,m,v);
- return 0;
-}
-
-static int ctrl_subsys_stream_get(struct pvr2_ctrl *cptr,int *vp)
-{
- *vp = cptr->hdw->subsys_stream_mask;
- return 0;
-}
-
-static int ctrl_subsys_stream_set(struct pvr2_ctrl *cptr,int m,int v)
-{
- pvr2_hdw_subsys_stream_bit_chg_no_lock(cptr->hdw,m,v);
- return 0;
-}
static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v)
{
@@ -915,6 +885,11 @@ static const struct pvr2_ctl_info control_defs[] = {
.get_value = ctrl_hsm_get,
DEFENUM(control_values_hsm),
},{
+ .desc = "Master State",
+ .name = "master_state",
+ .get_value = ctrl_masterstate_get,
+ DEFENUM(pvr2_state_names),
+ },{
.desc = "Signal Present",
.name = "signal_present",
.get_value = ctrl_signal_get,
@@ -955,20 +930,6 @@ static const struct pvr2_ctl_info control_defs[] = {
.sym_to_val = ctrl_std_sym_to_val,
.type = pvr2_ctl_bitmask,
},{
- .desc = "Subsystem enabled mask",
- .name = "debug_subsys_mask",
- .skip_init = !0,
- .get_value = ctrl_subsys_get,
- .set_value = ctrl_subsys_set,
- DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem),
- },{
- .desc = "Subsystem stream mask",
- .name = "debug_subsys_stream_mask",
- .skip_init = !0,
- .get_value = ctrl_subsys_stream_get,
- .set_value = ctrl_subsys_stream_set,
- DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem),
- },{
.desc = "Video Standard Name",
.name = "video_standard",
.internal_id = PVR2_CID_STDENUM,
@@ -1129,25 +1090,13 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
unsigned int pipe;
int ret;
u16 address;
- static const char *fw_files_29xxx[] = {
- "v4l-pvrusb2-29xxx-01.fw",
- };
- static const char *fw_files_24xxx[] = {
- "v4l-pvrusb2-24xxx-01.fw",
- };
- static const struct pvr2_string_table fw_file_defs[] = {
- [PVR2_HDW_TYPE_29XXX] = {
- fw_files_29xxx, ARRAY_SIZE(fw_files_29xxx)
- },
- [PVR2_HDW_TYPE_24XXX] = {
- fw_files_24xxx, ARRAY_SIZE(fw_files_24xxx)
- },
- };
- if ((hdw->hdw_type >= ARRAY_SIZE(fw_file_defs)) ||
- (!fw_file_defs[hdw->hdw_type].lst)) {
+ if (!hdw->hdw_desc->fx2_firmware.cnt) {
hdw->fw1_state = FW1_STATE_OK;
- return 0;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Connected device type defines"
+ " no firmware to upload; ignoring firmware");
+ return -ENOTTY;
}
hdw->fw1_state = FW1_STATE_FAILED; // default result
@@ -1155,8 +1104,8 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
trace_firmware("pvr2_upload_firmware1");
ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller",
- fw_file_defs[hdw->hdw_type].cnt,
- fw_file_defs[hdw->hdw_type].lst);
+ hdw->hdw_desc->fx2_firmware.cnt,
+ hdw->hdw_desc->fx2_firmware.lst);
if (ret < 0) {
if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING;
return ret;
@@ -1231,8 +1180,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
CX2341X_FIRM_ENC_FILENAME,
};
- if ((hdw->hdw_type != PVR2_HDW_TYPE_29XXX) &&
- (hdw->hdw_type != PVR2_HDW_TYPE_24XXX)) {
+ if (hdw->hdw_desc->flag_skip_cx23416_firmware) {
return 0;
}
@@ -1248,8 +1196,6 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
time we configure the encoder, then we'll fully configure it. */
hdw->enc_cur_valid = 0;
- hdw->flag_encoder_ok = 0;
-
/* First prepare firmware loading */
ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/
ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/
@@ -1347,293 +1293,129 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"firmware2 upload post-proc failure");
- } else {
- hdw->flag_encoder_ok = !0;
- hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_FIRMWARE);
}
return ret;
}
-#define FIRMWARE_RECOVERY_BITS \
- ((1<<PVR2_SUBSYS_B_ENC_CFG) | \
- (1<<PVR2_SUBSYS_B_ENC_RUN) | \
- (1<<PVR2_SUBSYS_B_ENC_FIRMWARE) | \
- (1<<PVR2_SUBSYS_B_USBSTREAM_RUN))
-
-/*
-
- This single function is key to pretty much everything. The pvrusb2
- device can logically be viewed as a series of subsystems which can be
- stopped / started or unconfigured / configured. To get things streaming,
- one must configure everything and start everything, but there may be
- various reasons over time to deconfigure something or stop something.
- This function handles all of this activity. Everything EVERYWHERE that
- must affect a subsystem eventually comes here to do the work.
-
- The current state of all subsystems is represented by a single bit mask,
- known as subsys_enabled_mask. The bit positions are defined by the
- PVR2_SUBSYS_xxxx macros, with one subsystem per bit position. At any
- time the set of configured or active subsystems can be queried just by
- looking at that mask. To change bits in that mask, this function here
- must be called. The "msk" argument indicates which bit positions to
- change, and the "val" argument defines the new values for the positions
- defined by "msk".
-
- There is a priority ordering of starting / stopping things, and for
- multiple requested changes, this function implements that ordering.
- (Thus we will act on a request to load encoder firmware before we
- configure the encoder.) In addition to priority ordering, there is a
- recovery strategy implemented here. If a particular step fails and we
- detect that failure, this function will clear the affected subsystem bits
- and restart. Thus we have a means for recovering from a dead encoder:
- Clear all bits that correspond to subsystems that we need to restart /
- reconfigure and start over.
-
-*/
-static void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
- unsigned long msk,
- unsigned long val)
-{
- unsigned long nmsk;
- unsigned long vmsk;
- int ret;
- unsigned int tryCount = 0;
-
- if (!hdw->flag_ok) return;
-
- msk &= PVR2_SUBSYS_ALL;
- nmsk = (hdw->subsys_enabled_mask & ~msk) | (val & msk);
- nmsk &= PVR2_SUBSYS_ALL;
-
- for (;;) {
- tryCount++;
- if (!((nmsk ^ hdw->subsys_enabled_mask) &
- PVR2_SUBSYS_ALL)) break;
- if (tryCount > 4) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Too many retries when configuring device;"
- " giving up");
- pvr2_hdw_render_useless(hdw);
- break;
- }
- if (tryCount > 1) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Retrying device reconfiguration");
- }
- pvr2_trace(PVR2_TRACE_INIT,
- "subsys mask changing 0x%lx:0x%lx"
- " from 0x%lx to 0x%lx",
- msk,val,hdw->subsys_enabled_mask,nmsk);
-
- vmsk = (nmsk ^ hdw->subsys_enabled_mask) &
- hdw->subsys_enabled_mask;
- if (vmsk) {
- if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " pvr2_encoder_stop");
- ret = pvr2_encoder_stop(hdw);
- if (ret) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Error recovery initiated");
- hdw->subsys_enabled_mask &=
- ~FIRMWARE_RECOVERY_BITS;
- continue;
- }
- }
- if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " pvr2_hdw_cmd_usbstream(0)");
- pvr2_hdw_cmd_usbstream(hdw,0);
- }
- if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " decoder disable");
- if (hdw->decoder_ctrl) {
- hdw->decoder_ctrl->enable(
- hdw->decoder_ctrl->ctxt,0);
- } else {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "WARNING:"
- " No decoder present");
- }
- hdw->subsys_enabled_mask &=
- ~(1<<PVR2_SUBSYS_B_DIGITIZER_RUN);
- }
- if (vmsk & PVR2_SUBSYS_CFG_ALL) {
- hdw->subsys_enabled_mask &=
- ~(vmsk & PVR2_SUBSYS_CFG_ALL);
- }
- }
- vmsk = (nmsk ^ hdw->subsys_enabled_mask) & nmsk;
- if (vmsk) {
- if (vmsk & (1<<PVR2_SUBSYS_B_ENC_FIRMWARE)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " pvr2_upload_firmware2");
- ret = pvr2_upload_firmware2(hdw);
- if (ret) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Failure uploading encoder"
- " firmware");
- pvr2_hdw_render_useless(hdw);
- break;
- }
- }
- if (vmsk & (1<<PVR2_SUBSYS_B_ENC_CFG)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " pvr2_encoder_configure");
- ret = pvr2_encoder_configure(hdw);
- if (ret) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Error recovery initiated");
- hdw->subsys_enabled_mask &=
- ~FIRMWARE_RECOVERY_BITS;
- continue;
- }
- }
- if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " decoder enable");
- if (hdw->decoder_ctrl) {
- hdw->decoder_ctrl->enable(
- hdw->decoder_ctrl->ctxt,!0);
- } else {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "WARNING:"
- " No decoder present");
- }
- hdw->subsys_enabled_mask |=
- (1<<PVR2_SUBSYS_B_DIGITIZER_RUN);
- }
- if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " pvr2_hdw_cmd_usbstream(1)");
- pvr2_hdw_cmd_usbstream(hdw,!0);
- }
- if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) {
- pvr2_trace(PVR2_TRACE_CTL,
- "/*---TRACE_CTL----*/"
- " pvr2_encoder_start");
- ret = pvr2_encoder_start(hdw);
- if (ret) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Error recovery initiated");
- hdw->subsys_enabled_mask &=
- ~FIRMWARE_RECOVERY_BITS;
- continue;
- }
- }
- }
+static const char *pvr2_get_state_name(unsigned int st)
+{
+ if (st < ARRAY_SIZE(pvr2_state_names)) {
+ return pvr2_state_names[st];
}
+ return "???";
}
-
-void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw,
- unsigned long msk,unsigned long val)
+static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl)
{
- LOCK_TAKE(hdw->big_lock); do {
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,msk,val);
- } while (0); LOCK_GIVE(hdw->big_lock);
+ if (!hdw->decoder_ctrl) {
+ if (!hdw->flag_decoder_missed) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "WARNING: No decoder present");
+ hdw->flag_decoder_missed = !0;
+ trace_stbit("flag_decoder_missed",
+ hdw->flag_decoder_missed);
+ }
+ return -EIO;
+ }
+ hdw->decoder_ctrl->enable(hdw->decoder_ctrl->ctxt,enablefl);
+ return 0;
}
-unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *hdw)
+void pvr2_hdw_set_decoder(struct pvr2_hdw *hdw,struct pvr2_decoder_ctrl *ptr)
{
- return hdw->subsys_enabled_mask;
+ if (hdw->decoder_ctrl == ptr) return;
+ hdw->decoder_ctrl = ptr;
+ if (hdw->decoder_ctrl && hdw->flag_decoder_missed) {
+ hdw->flag_decoder_missed = 0;
+ trace_stbit("flag_decoder_missed",
+ hdw->flag_decoder_missed);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Decoder has appeared");
+ pvr2_hdw_state_sched(hdw);
+ }
}
-unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *hdw)
+int pvr2_hdw_get_state(struct pvr2_hdw *hdw)
{
- return hdw->subsys_stream_mask;
+ return hdw->master_state;
}
-static void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
- unsigned long msk,
- unsigned long val)
+static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *hdw)
{
- unsigned long val2;
- msk &= PVR2_SUBSYS_ALL;
- val2 = ((hdw->subsys_stream_mask & ~msk) | (val & msk));
- pvr2_trace(PVR2_TRACE_INIT,
- "stream mask changing 0x%lx:0x%lx from 0x%lx to 0x%lx",
- msk,val,hdw->subsys_stream_mask,val2);
- hdw->subsys_stream_mask = val2;
+ if (!hdw->flag_tripped) return 0;
+ hdw->flag_tripped = 0;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Clearing driver error statuss");
+ return !0;
}
-void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw,
- unsigned long msk,
- unsigned long val)
+int pvr2_hdw_untrip(struct pvr2_hdw *hdw)
{
+ int fl;
LOCK_TAKE(hdw->big_lock); do {
- pvr2_hdw_subsys_stream_bit_chg_no_lock(hdw,msk,val);
+ fl = pvr2_hdw_untrip_unlocked(hdw);
} while (0); LOCK_GIVE(hdw->big_lock);
+ if (fl) pvr2_hdw_state_sched(hdw);
+ return 0;
}
-static int pvr2_hdw_set_streaming_no_lock(struct pvr2_hdw *hdw,int enableFl)
+const char *pvr2_hdw_get_state_name(unsigned int id)
{
- if ((!enableFl) == !(hdw->flag_streaming_enabled)) return 0;
- if (enableFl) {
- pvr2_trace(PVR2_TRACE_START_STOP,
- "/*--TRACE_STREAM--*/ enable");
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,~0);
- } else {
- pvr2_trace(PVR2_TRACE_START_STOP,
- "/*--TRACE_STREAM--*/ disable");
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0);
- }
- if (!hdw->flag_ok) return -EIO;
- hdw->flag_streaming_enabled = enableFl != 0;
- return 0;
+ if (id >= ARRAY_SIZE(pvr2_state_names)) return NULL;
+ return pvr2_state_names[id];
}
int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw)
{
- return hdw->flag_streaming_enabled != 0;
+ return hdw->state_pipeline_req != 0;
}
int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag)
{
- int ret;
+ int ret,st;
LOCK_TAKE(hdw->big_lock); do {
- ret = pvr2_hdw_set_streaming_no_lock(hdw,enable_flag);
+ pvr2_hdw_untrip_unlocked(hdw);
+ if ((!enable_flag) != !(hdw->state_pipeline_req)) {
+ hdw->state_pipeline_req = enable_flag != 0;
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*--TRACE_STREAM--*/ %s",
+ enable_flag ? "enable" : "disable");
+ }
+ pvr2_hdw_state_sched(hdw);
} while (0); LOCK_GIVE(hdw->big_lock);
- return ret;
-}
-
-
-static int pvr2_hdw_set_stream_type_no_lock(struct pvr2_hdw *hdw,
- enum pvr2_config config)
-{
- unsigned long sm = hdw->subsys_enabled_mask;
- if (!hdw->flag_ok) return -EIO;
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0);
- hdw->config = config;
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,sm);
+ if ((ret = pvr2_hdw_wait(hdw,0)) < 0) return ret;
+ if (enable_flag) {
+ while ((st = hdw->master_state) != PVR2_STATE_RUN) {
+ if (st != PVR2_STATE_READY) return -EIO;
+ if ((ret = pvr2_hdw_wait(hdw,st)) < 0) return ret;
+ }
+ }
return 0;
}
int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config)
{
- int ret;
- if (!hdw->flag_ok) return -EIO;
+ int fl;
LOCK_TAKE(hdw->big_lock);
- ret = pvr2_hdw_set_stream_type_no_lock(hdw,config);
+ if ((fl = (hdw->desired_stream_type != config)) != 0) {
+ hdw->desired_stream_type = config;
+ hdw->state_pipeline_config = 0;
+ trace_stbit("state_pipeline_config",
+ hdw->state_pipeline_config);
+ pvr2_hdw_state_sched(hdw);
+ }
LOCK_GIVE(hdw->big_lock);
- return ret;
+ if (fl) return 0;
+ return pvr2_hdw_wait(hdw,0);
}
@@ -1646,6 +1428,7 @@ static int get_default_tuner_type(struct pvr2_hdw *hdw)
}
if (tp < 0) return -EINVAL;
hdw->tuner_type = tp;
+ hdw->tuner_updated = !0;
return 0;
}
@@ -1656,8 +1439,9 @@ static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw)
int tp = 0;
if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
tp = video_std[unit_number];
+ if (tp) return tp;
}
- return tp;
+ return 0;
}
@@ -1731,7 +1515,7 @@ const static struct pvr2_std_hack std_eeprom_maps[] = {
},
{ /* PAL(D/D1/K) */
.pat = V4L2_STD_DK,
- .std = V4L2_STD_PAL_D/V4L2_STD_PAL_D1|V4L2_STD_PAL_K,
+ .std = V4L2_STD_PAL_D|V4L2_STD_PAL_D1|V4L2_STD_PAL_K,
},
};
@@ -1739,18 +1523,20 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
{
char buf[40];
unsigned int bcnt;
- v4l2_std_id std1,std2;
+ v4l2_std_id std1,std2,std3;
std1 = get_default_standard(hdw);
+ std3 = std1 ? 0 : hdw->hdw_desc->default_std_mask;
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom);
pvr2_trace(PVR2_TRACE_STD,
- "Supported video standard(s) reported by eeprom: %.*s",
+ "Supported video standard(s) reported available"
+ " in hardware: %.*s",
bcnt,buf);
hdw->std_mask_avail = hdw->std_mask_eeprom;
- std2 = std1 & ~hdw->std_mask_avail;
+ std2 = (std1|std3) & ~hdw->std_mask_avail;
if (std2) {
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2);
pvr2_trace(PVR2_TRACE_STD,
@@ -1772,6 +1558,16 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
pvr2_hdw_internal_find_stdenum(hdw);
return;
}
+ if (std3) {
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std3);
+ pvr2_trace(PVR2_TRACE_STD,
+ "Initial video standard"
+ " (determined by device type): %.*s",bcnt,buf);
+ hdw->std_mask_cur = std3;
+ hdw->std_dirty = !0;
+ pvr2_hdw_internal_find_stdenum(hdw);
+ return;
+ }
{
unsigned int idx;
@@ -1816,8 +1612,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
unsigned int idx;
struct pvr2_ctrl *cptr;
int reloadFl = 0;
- if ((hdw->hdw_type == PVR2_HDW_TYPE_29XXX) ||
- (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) {
+ if (hdw->hdw_desc->fx2_firmware.cnt) {
if (!reloadFl) {
reloadFl =
(hdw->usb_intf->cur_altsetting->desc.bNumEndpoints
@@ -1853,25 +1648,13 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
}
if (!pvr2_hdw_dev_ok(hdw)) return;
- if (hdw->hdw_type < ARRAY_SIZE(pvr2_client_lists)) {
- for (idx = 0;
- idx < pvr2_client_lists[hdw->hdw_type].cnt;
- idx++) {
- request_module(
- pvr2_client_lists[hdw->hdw_type].lst[idx]);
- }
+ for (idx = 0; idx < hdw->hdw_desc->client_modules.cnt; idx++) {
+ request_module(hdw->hdw_desc->client_modules.lst[idx]);
}
- if ((hdw->hdw_type == PVR2_HDW_TYPE_29XXX) ||
- (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) {
+ if (!hdw->hdw_desc->flag_no_powerup) {
pvr2_hdw_cmd_powerup(hdw);
if (!pvr2_hdw_dev_ok(hdw)) return;
-
- if (pvr2_upload_firmware2(hdw)){
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,"device unstable!!");
- pvr2_hdw_render_useless(hdw);
- return;
- }
}
// This step MUST happen after the earlier powerup step.
@@ -1899,15 +1682,22 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
// thread-safe against the normal pvr2_send_request() mechanism.
// (We should make it thread safe).
- ret = pvr2_hdw_get_eeprom_addr(hdw);
- if (!pvr2_hdw_dev_ok(hdw)) return;
- if (ret < 0) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Unable to determine location of eeprom, skipping");
- } else {
- hdw->eeprom_addr = ret;
- pvr2_eeprom_analyze(hdw);
+ if (hdw->hdw_desc->flag_has_hauppauge_rom) {
+ ret = pvr2_hdw_get_eeprom_addr(hdw);
if (!pvr2_hdw_dev_ok(hdw)) return;
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Unable to determine location of eeprom,"
+ " skipping");
+ } else {
+ hdw->eeprom_addr = ret;
+ pvr2_eeprom_analyze(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+ }
+ } else {
+ hdw->tuner_type = hdw->hdw_desc->default_tuner_type;
+ hdw->tuner_updated = !0;
+ hdw->std_mask_eeprom = V4L2_STD_ALL;
}
pvr2_hdw_setup_std(hdw);
@@ -1918,14 +1708,12 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
hdw->tuner_type);
}
- hdw->tuner_updated = !0;
pvr2_i2c_core_check_stale(hdw);
hdw->tuner_updated = 0;
if (!pvr2_hdw_dev_ok(hdw)) return;
- pvr2_hdw_commit_ctl_internal(hdw);
- if (!pvr2_hdw_dev_ok(hdw)) return;
+ pvr2_hdw_commit_setup(hdw);
hdw->vid_stream = pvr2_stream_create();
if (!pvr2_hdw_dev_ok(hdw)) return;
@@ -1945,25 +1733,25 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
if (!pvr2_hdw_dev_ok(hdw)) return;
- /* Make sure everything is up to date */
- pvr2_i2c_core_sync(hdw);
-
- if (!pvr2_hdw_dev_ok(hdw)) return;
-
hdw->flag_init_ok = !0;
+
+ pvr2_hdw_state_sched(hdw);
}
-int pvr2_hdw_setup(struct pvr2_hdw *hdw)
+/* Set up the structure and attempt to put the device into a usable state.
+ This can be a time-consuming operation, which is why it is not done
+ internally as part of the create() step. */
+static void pvr2_hdw_setup(struct pvr2_hdw *hdw)
{
pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw);
- LOCK_TAKE(hdw->big_lock); do {
+ do {
pvr2_hdw_setup_low(hdw);
pvr2_trace(PVR2_TRACE_INIT,
"pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d",
- hdw,hdw->flag_ok,hdw->flag_init_ok);
+ hdw,pvr2_hdw_dev_ok(hdw),hdw->flag_init_ok);
if (pvr2_hdw_dev_ok(hdw)) {
- if (pvr2_hdw_init_ok(hdw)) {
+ if (hdw->flag_init_ok) {
pvr2_trace(
PVR2_TRACE_INFO,
"Device initialization"
@@ -2013,9 +1801,8 @@ int pvr2_hdw_setup(struct pvr2_hdw *hdw)
" the pvrusb2 device"
" in order to recover.");
}
- } while (0); LOCK_GIVE(hdw->big_lock);
+ } while (0);
pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw);
- return hdw->flag_init_ok;
}
@@ -2026,24 +1813,32 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
{
unsigned int idx,cnt1,cnt2;
struct pvr2_hdw *hdw;
- unsigned int hdw_type;
int valid_std_mask;
struct pvr2_ctrl *cptr;
+ const struct pvr2_device_desc *hdw_desc;
__u8 ifnum;
struct v4l2_queryctrl qctrl;
struct pvr2_ctl_info *ciptr;
- hdw_type = devid - pvr2_device_table;
- if (hdw_type >= ARRAY_SIZE(pvr2_device_names)) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Bogus device type of %u reported",hdw_type);
- return NULL;
- }
+ hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info);
hdw = kzalloc(sizeof(*hdw),GFP_KERNEL);
pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
- hdw,pvr2_device_names[hdw_type]);
+ hdw,hdw_desc->description);
if (!hdw) goto fail;
+
+ init_timer(&hdw->quiescent_timer);
+ hdw->quiescent_timer.data = (unsigned long)hdw;
+ hdw->quiescent_timer.function = pvr2_hdw_quiescent_timeout;
+
+ init_timer(&hdw->encoder_wait_timer);
+ hdw->encoder_wait_timer.data = (unsigned long)hdw;
+ hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout;
+
+ hdw->master_state = PVR2_STATE_DEAD;
+
+ init_waitqueue_head(&hdw->state_wait_data);
+
hdw->tuner_signal_stale = !0;
cx2341x_fill_defaults(&hdw->enc_ctl_state);
@@ -2052,7 +1847,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt,
GFP_KERNEL);
if (!hdw->controls) goto fail;
- hdw->hdw_type = hdw_type;
+ hdw->hdw_desc = hdw_desc;
for (idx = 0; idx < hdw->control_cnt; idx++) {
cptr = hdw->controls + idx;
cptr->hdw = hdw;
@@ -2184,18 +1979,16 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1;
hdw->name[cnt1] = 0;
+ hdw->workqueue = create_singlethread_workqueue(hdw->name);
+ INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll);
+ INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c);
+ INIT_WORK(&hdw->workinit,pvr2_hdw_worker_init);
+
pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
hdw->unit_number,hdw->name);
hdw->tuner_type = -1;
hdw->flag_ok = !0;
- /* Initialize the mask of subsystems that we will shut down when we
- stop streaming. */
- hdw->subsys_stream_mask = PVR2_SUBSYS_RUN_ALL;
- hdw->subsys_stream_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
-
- pvr2_trace(PVR2_TRACE_INIT,"subsys_stream_mask: 0x%lx",
- hdw->subsys_stream_mask);
hdw->usb_intf = intf;
hdw->usb_dev = interface_to_usbdev(intf);
@@ -2211,15 +2004,25 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
mutex_init(&hdw->ctl_lock_mutex);
mutex_init(&hdw->big_lock_mutex);
+ queue_work(hdw->workqueue,&hdw->workinit);
return hdw;
fail:
if (hdw) {
+ del_timer_sync(&hdw->quiescent_timer);
+ del_timer_sync(&hdw->encoder_wait_timer);
+ if (hdw->workqueue) {
+ flush_workqueue(hdw->workqueue);
+ destroy_workqueue(hdw->workqueue);
+ hdw->workqueue = NULL;
+ }
usb_free_urb(hdw->ctl_read_urb);
usb_free_urb(hdw->ctl_write_urb);
kfree(hdw->ctl_read_buffer);
kfree(hdw->ctl_write_buffer);
kfree(hdw->controls);
kfree(hdw->mpeg_ctrl_info);
+ kfree(hdw->std_defs);
+ kfree(hdw->std_enum_names);
kfree(hdw);
}
return NULL;
@@ -2250,10 +2053,10 @@ static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
kfree(hdw->ctl_write_buffer);
hdw->ctl_write_buffer = NULL;
}
- pvr2_hdw_render_useless_unlocked(hdw);
hdw->flag_disconnected = !0;
hdw->usb_dev = NULL;
hdw->usb_intf = NULL;
+ pvr2_hdw_render_useless(hdw);
}
@@ -2262,6 +2065,13 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
{
if (!hdw) return;
pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw);
+ del_timer_sync(&hdw->quiescent_timer);
+ del_timer_sync(&hdw->encoder_wait_timer);
+ if (hdw->workqueue) {
+ flush_workqueue(hdw->workqueue);
+ destroy_workqueue(hdw->workqueue);
+ hdw->workqueue = NULL;
+ }
if (hdw->fw_buffer) {
kfree(hdw->fw_buffer);
hdw->fw_buffer = NULL;
@@ -2290,12 +2100,6 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
}
-int pvr2_hdw_init_ok(struct pvr2_hdw *hdw)
-{
- return hdw->flag_init_ok;
-}
-
-
int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw)
{
return (hdw && hdw->flag_ok);
@@ -2473,17 +2277,11 @@ static const char *get_ctrl_typename(enum pvr2_ctl_type tp)
}
-/* Commit all control changes made up to this point. Subsystems can be
- indirectly affected by these changes. For a given set of things being
- committed, we'll clear the affected subsystem bits and then once we're
- done committing everything we'll make a request to restore the subsystem
- state(s) back to their previous value before this function was called.
- Thus we can automatically reconfigure affected pieces of the driver as
- controls are changed. */
-static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
+/* Figure out if we need to commit control changes. If so, mark internal
+ state flags to indicate this fact and return true. Otherwise do nothing
+ else and return false. */
+static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw)
{
- unsigned long saved_subsys_mask = hdw->subsys_enabled_mask;
- unsigned long stale_subsys_mask = 0;
unsigned int idx;
struct pvr2_ctrl *cptr;
int value;
@@ -2518,6 +2316,25 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
return 0;
}
+ hdw->state_pipeline_config = 0;
+ trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
+ pvr2_hdw_state_sched(hdw);
+
+ return !0;
+}
+
+
+/* Perform all operations needed to commit all control changes. This must
+ be performed in synchronization with the pipeline state and is thus
+ expected to be called as part of the driver's worker thread. Return
+ true if commit successful, otherwise return false to indicate that
+ commit isn't possible at this time. */
+static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
+{
+ unsigned int idx;
+ struct pvr2_ctrl *cptr;
+ int disruptive_change;
+
/* When video standard changes, reset the hres and vres values -
but if the user has pending changes there, then let the changes
take priority. */
@@ -2536,24 +2353,26 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
}
}
- if (hdw->std_dirty ||
- hdw->enc_stale ||
- hdw->srate_dirty ||
- hdw->res_ver_dirty ||
- hdw->res_hor_dirty ||
- 0) {
- /* If any of this changes, then the encoder needs to be
- reconfigured, and we need to reset the stream. */
- stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
- }
-
- if (hdw->input_dirty) {
- /* pk: If input changes to or from radio, then the encoder
- needs to be restarted (for ENC_MUTE_VIDEO to work) */
- stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN);
+ /* If any of the below has changed, then we can't do the update
+ while the pipeline is running. Pipeline must be paused first
+ and decoder -> encoder connection be made quiescent before we
+ can proceed. */
+ disruptive_change =
+ (hdw->std_dirty ||
+ hdw->enc_unsafe_stale ||
+ hdw->srate_dirty ||
+ hdw->res_ver_dirty ||
+ hdw->res_hor_dirty ||
+ hdw->input_dirty ||
+ (hdw->active_stream_type != hdw->desired_stream_type));
+ if (disruptive_change && !hdw->state_pipeline_idle) {
+ /* Pipeline is not idle; we can't proceed. Arrange to
+ cause pipeline to stop so that we can try this again
+ later.... */
+ hdw->state_pipeline_pause = !0;
+ return 0;
}
-
if (hdw->srate_dirty) {
/* Write new sample rate into control structure since
* the master copy is stale. We must track srate
@@ -2582,51 +2401,88 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
cptr->info->clear_dirty(cptr);
}
+ if (hdw->active_stream_type != hdw->desired_stream_type) {
+ /* Handle any side effects of stream config here */
+ hdw->active_stream_type = hdw->desired_stream_type;
+ }
+
/* Now execute i2c core update */
pvr2_i2c_core_sync(hdw);
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,stale_subsys_mask,0);
- pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,saved_subsys_mask);
+ if (hdw->state_encoder_run) {
+ /* If encoder isn't running, then this will get worked out
+ later when we start the encoder. */
+ if (pvr2_encoder_adjust(hdw) < 0) return !0;
+ }
- return 0;
+ hdw->state_pipeline_config = !0;
+ trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
+ return !0;
}
int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw)
{
+ int fl;
+ LOCK_TAKE(hdw->big_lock);
+ fl = pvr2_hdw_commit_setup(hdw);
+ LOCK_GIVE(hdw->big_lock);
+ if (!fl) return 0;
+ return pvr2_hdw_wait(hdw,0);
+}
+
+
+static void pvr2_hdw_worker_i2c(struct work_struct *work)
+{
+ struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,worki2csync);
LOCK_TAKE(hdw->big_lock); do {
- pvr2_hdw_commit_ctl_internal(hdw);
+ pvr2_i2c_core_sync(hdw);
} while (0); LOCK_GIVE(hdw->big_lock);
- return 0;
}
-void pvr2_hdw_poll(struct pvr2_hdw *hdw)
+static void pvr2_hdw_worker_poll(struct work_struct *work)
{
+ int fl = 0;
+ struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workpoll);
LOCK_TAKE(hdw->big_lock); do {
- pvr2_i2c_core_sync(hdw);
+ fl = pvr2_hdw_state_eval(hdw);
} while (0); LOCK_GIVE(hdw->big_lock);
+ if (fl && hdw->state_func) {
+ hdw->state_func(hdw->state_data);
+ }
}
-void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *hdw,
- void (*func)(void *),
- void *data)
+static void pvr2_hdw_worker_init(struct work_struct *work)
{
+ struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workinit);
LOCK_TAKE(hdw->big_lock); do {
- hdw->poll_trigger_func = func;
- hdw->poll_trigger_data = data;
+ pvr2_hdw_setup(hdw);
} while (0); LOCK_GIVE(hdw->big_lock);
}
-void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *hdw)
+static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state)
{
- if (hdw->poll_trigger_func) {
- hdw->poll_trigger_func(hdw->poll_trigger_data);
- }
+ return wait_event_interruptible(
+ hdw->state_wait_data,
+ (hdw->state_stale == 0) &&
+ (!state || (hdw->master_state != state)));
}
+
+void pvr2_hdw_set_state_callback(struct pvr2_hdw *hdw,
+ void (*callback_func)(void *),
+ void *callback_data)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ hdw->state_data = callback_data;
+ hdw->state_func = callback_func;
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
/* Return name for this driver instance */
const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
{
@@ -2634,6 +2490,18 @@ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
}
+const char *pvr2_hdw_get_desc(struct pvr2_hdw *hdw)
+{
+ return hdw->hdw_desc->description;
+}
+
+
+const char *pvr2_hdw_get_type(struct pvr2_hdw *hdw)
+{
+ return hdw->hdw_desc->shortname;
+}
+
+
int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw)
{
int result;
@@ -2689,6 +2557,7 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
pvr2_i2c_core_sync(hdw);
pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:");
cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2");
+ pvr2_hdw_state_log_state(hdw);
printk(KERN_INFO "pvrusb2: ================== END STATUS CARD #%d ==================\n", nr);
} while (0); LOCK_GIVE(hdw->big_lock);
}
@@ -2959,7 +2828,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
" without lock!!");
return -EDEADLK;
}
- if ((!hdw->flag_ok) && !probe_fl) {
+ if (!hdw->flag_ok && !probe_fl) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"Attempted to execute control transfer"
" when device not ok");
@@ -3167,7 +3036,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
hdw->cmd_debug_state = 0;
if ((status < 0) && (!probe_fl)) {
- pvr2_hdw_render_useless_unlocked(hdw);
+ pvr2_hdw_render_useless(hdw);
}
return status;
}
@@ -3227,24 +3096,17 @@ static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
}
-static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw)
+void pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
{
if (!hdw->flag_ok) return;
- pvr2_trace(PVR2_TRACE_INIT,"render_useless");
- hdw->flag_ok = 0;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Device being rendered inoperable");
if (hdw->vid_stream) {
pvr2_stream_setup(hdw->vid_stream,NULL,0,0);
}
- hdw->flag_streaming_enabled = 0;
- hdw->subsys_enabled_mask = 0;
-}
-
-
-void pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
-{
- LOCK_TAKE(hdw->ctl_lock);
- pvr2_hdw_render_useless_unlocked(hdw);
- LOCK_GIVE(hdw->ctl_lock);
+ hdw->flag_ok = 0;
+ trace_stbit("flag_ok",hdw->flag_ok);
+ pvr2_hdw_state_sched(hdw);
}
@@ -3299,7 +3161,6 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw)
int status;
LOCK_TAKE(hdw->ctl_lock); do {
pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset");
- hdw->flag_ok = !0;
hdw->cmd_buffer[0] = FX2CMD_DEEP_RESET;
status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
} while (0); LOCK_GIVE(hdw->ctl_lock);
@@ -3349,26 +3210,473 @@ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
(runFl ? FX2CMD_STREAMING_ON : FX2CMD_STREAMING_OFF);
status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
} while (0); LOCK_GIVE(hdw->ctl_lock);
- if (!status) {
- hdw->subsys_enabled_mask =
- ((hdw->subsys_enabled_mask &
- ~(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) |
- (runFl ? (1<<PVR2_SUBSYS_B_USBSTREAM_RUN) : 0));
- }
return status;
}
-void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
- struct pvr2_hdw_debug_info *ptr)
+/* Evaluate whether or not state_encoder_ok can change */
+static int state_eval_encoder_ok(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_encoder_ok) return 0;
+ if (hdw->flag_tripped) return 0;
+ if (hdw->state_encoder_run) return 0;
+ if (hdw->state_encoder_config) return 0;
+ if (hdw->state_decoder_run) return 0;
+ if (hdw->state_usbstream_run) return 0;
+ if (pvr2_upload_firmware2(hdw) < 0) {
+ hdw->flag_tripped = !0;
+ trace_stbit("flag_tripped",hdw->flag_tripped);
+ return !0;
+ }
+ hdw->state_encoder_ok = !0;
+ trace_stbit("state_encoder_ok",hdw->state_encoder_ok);
+ return !0;
+}
+
+
+/* Evaluate whether or not state_encoder_config can change */
+static int state_eval_encoder_config(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_encoder_config) {
+ if (hdw->state_encoder_ok) {
+ if (hdw->state_pipeline_req &&
+ !hdw->state_pipeline_pause) return 0;
+ }
+ hdw->state_encoder_config = 0;
+ hdw->state_encoder_waitok = 0;
+ trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
+ /* paranoia - solve race if timer just completed */
+ del_timer_sync(&hdw->encoder_wait_timer);
+ } else {
+ if (!hdw->state_encoder_ok ||
+ !hdw->state_pipeline_idle ||
+ hdw->state_pipeline_pause ||
+ !hdw->state_pipeline_req ||
+ !hdw->state_pipeline_config) {
+ /* We must reset the enforced wait interval if
+ anything has happened that might have disturbed
+ the encoder. This should be a rare case. */
+ if (timer_pending(&hdw->encoder_wait_timer)) {
+ del_timer_sync(&hdw->encoder_wait_timer);
+ }
+ if (hdw->state_encoder_waitok) {
+ /* Must clear the state - therefore we did
+ something to a state bit and must also
+ return true. */
+ hdw->state_encoder_waitok = 0;
+ trace_stbit("state_encoder_waitok",
+ hdw->state_encoder_waitok);
+ return !0;
+ }
+ return 0;
+ }
+ if (!hdw->state_encoder_waitok) {
+ if (!timer_pending(&hdw->encoder_wait_timer)) {
+ /* waitok flag wasn't set and timer isn't
+ running. Check flag once more to avoid
+ a race then start the timer. This is
+ the point when we measure out a minimal
+ quiet interval before doing something to
+ the encoder. */
+ if (!hdw->state_encoder_waitok) {
+ hdw->encoder_wait_timer.expires =
+ jiffies + (HZ*50/1000);
+ add_timer(&hdw->encoder_wait_timer);
+ }
+ }
+ /* We can't continue until we know we have been
+ quiet for the interval measured by this
+ timer. */
+ return 0;
+ }
+ pvr2_encoder_configure(hdw);
+ if (hdw->state_encoder_ok) hdw->state_encoder_config = !0;
+ }
+ trace_stbit("state_encoder_config",hdw->state_encoder_config);
+ return !0;
+}
+
+
+/* Evaluate whether or not state_encoder_run can change */
+static int state_eval_encoder_run(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_encoder_run) {
+ if (hdw->state_encoder_ok) {
+ if (hdw->state_decoder_run) return 0;
+ if (pvr2_encoder_stop(hdw) < 0) return !0;
+ }
+ hdw->state_encoder_run = 0;
+ } else {
+ if (!hdw->state_encoder_ok) return 0;
+ if (!hdw->state_decoder_run) return 0;
+ if (pvr2_encoder_start(hdw) < 0) return !0;
+ hdw->state_encoder_run = !0;
+ }
+ trace_stbit("state_encoder_run",hdw->state_encoder_run);
+ return !0;
+}
+
+
+/* Timeout function for quiescent timer. */
+static void pvr2_hdw_quiescent_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ hdw->state_decoder_quiescent = !0;
+ trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
+ hdw->state_stale = !0;
+ queue_work(hdw->workqueue,&hdw->workpoll);
+}
+
+
+/* Timeout function for encoder wait timer. */
+static void pvr2_hdw_encoder_wait_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ hdw->state_encoder_waitok = !0;
+ trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
+ hdw->state_stale = !0;
+ queue_work(hdw->workqueue,&hdw->workpoll);
+}
+
+
+/* Evaluate whether or not state_decoder_run can change */
+static int state_eval_decoder_run(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_decoder_run) {
+ if (hdw->state_encoder_ok) {
+ if (hdw->state_pipeline_req &&
+ !hdw->state_pipeline_pause) return 0;
+ }
+ if (!hdw->flag_decoder_missed) {
+ pvr2_decoder_enable(hdw,0);
+ }
+ hdw->state_decoder_quiescent = 0;
+ hdw->state_decoder_run = 0;
+ /* paranoia - solve race if timer just completed */
+ del_timer_sync(&hdw->quiescent_timer);
+ } else {
+ if (!hdw->state_decoder_quiescent) {
+ if (!timer_pending(&hdw->quiescent_timer)) {
+ /* We don't do something about the
+ quiescent timer until right here because
+ we also want to catch cases where the
+ decoder was already not running (like
+ after initialization) as opposed to
+ knowing that we had just stopped it.
+ The second flag check is here to cover a
+ race - the timer could have run and set
+ this flag just after the previous check
+ but before we did the pending check. */
+ if (!hdw->state_decoder_quiescent) {
+ hdw->quiescent_timer.expires =
+ jiffies + (HZ*50/1000);
+ add_timer(&hdw->quiescent_timer);
+ }
+ }
+ /* Don't allow decoder to start again until it has
+ been quiesced first. This little detail should
+ hopefully further stabilize the encoder. */
+ return 0;
+ }
+ if (!hdw->state_pipeline_req ||
+ hdw->state_pipeline_pause ||
+ !hdw->state_pipeline_config ||
+ !hdw->state_encoder_config ||
+ !hdw->state_encoder_ok) return 0;
+ del_timer_sync(&hdw->quiescent_timer);
+ if (hdw->flag_decoder_missed) return 0;
+ if (pvr2_decoder_enable(hdw,!0) < 0) return 0;
+ hdw->state_decoder_quiescent = 0;
+ hdw->state_decoder_run = !0;
+ }
+ trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
+ trace_stbit("state_decoder_run",hdw->state_decoder_run);
+ return !0;
+}
+
+
+/* Evaluate whether or not state_usbstream_run can change */
+static int state_eval_usbstream_run(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_usbstream_run) {
+ if (hdw->state_encoder_ok) {
+ if (hdw->state_encoder_run) return 0;
+ }
+ pvr2_hdw_cmd_usbstream(hdw,0);
+ hdw->state_usbstream_run = 0;
+ } else {
+ if (!hdw->state_encoder_ok ||
+ !hdw->state_encoder_run ||
+ !hdw->state_pipeline_req ||
+ hdw->state_pipeline_pause) return 0;
+ if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0;
+ hdw->state_usbstream_run = !0;
+ }
+ trace_stbit("state_usbstream_run",hdw->state_usbstream_run);
+ return !0;
+}
+
+
+/* Attempt to configure pipeline, if needed */
+static int state_eval_pipeline_config(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_pipeline_config ||
+ hdw->state_pipeline_pause) return 0;
+ pvr2_hdw_commit_execute(hdw);
+ return !0;
+}
+
+
+/* Update pipeline idle and pipeline pause tracking states based on other
+ inputs. This must be called whenever the other relevant inputs have
+ changed. */
+static int state_update_pipeline_state(struct pvr2_hdw *hdw)
+{
+ unsigned int st;
+ int updatedFl = 0;
+ /* Update pipeline state */
+ st = !(hdw->state_encoder_run ||
+ hdw->state_decoder_run ||
+ hdw->state_usbstream_run ||
+ (!hdw->state_decoder_quiescent));
+ if (!st != !hdw->state_pipeline_idle) {
+ hdw->state_pipeline_idle = st;
+ updatedFl = !0;
+ }
+ if (hdw->state_pipeline_idle && hdw->state_pipeline_pause) {
+ hdw->state_pipeline_pause = 0;
+ updatedFl = !0;
+ }
+ return updatedFl;
+}
+
+
+typedef int (*state_eval_func)(struct pvr2_hdw *);
+
+/* Set of functions to be run to evaluate various states in the driver. */
+const static state_eval_func eval_funcs[] = {
+ state_eval_pipeline_config,
+ state_eval_encoder_ok,
+ state_eval_encoder_config,
+ state_eval_decoder_run,
+ state_eval_encoder_run,
+ state_eval_usbstream_run,
+};
+
+
+/* Process various states and return true if we did anything interesting. */
+static int pvr2_hdw_state_update(struct pvr2_hdw *hdw)
+{
+ unsigned int i;
+ int state_updated = 0;
+ int check_flag;
+
+ if (!hdw->state_stale) return 0;
+ if ((hdw->fw1_state != FW1_STATE_OK) ||
+ !hdw->flag_ok) {
+ hdw->state_stale = 0;
+ return !0;
+ }
+ /* This loop is the heart of the entire driver. It keeps trying to
+ evaluate various bits of driver state until nothing changes for
+ one full iteration. Each "bit of state" tracks some global
+ aspect of the driver, e.g. whether decoder should run, if
+ pipeline is configured, usb streaming is on, etc. We separately
+ evaluate each of those questions based on other driver state to
+ arrive at the correct running configuration. */
+ do {
+ check_flag = 0;
+ state_update_pipeline_state(hdw);
+ /* Iterate over each bit of state */
+ for (i = 0; (i<ARRAY_SIZE(eval_funcs)) && hdw->flag_ok; i++) {
+ if ((*eval_funcs[i])(hdw)) {
+ check_flag = !0;
+ state_updated = !0;
+ state_update_pipeline_state(hdw);
+ }
+ }
+ } while (check_flag && hdw->flag_ok);
+ hdw->state_stale = 0;
+ trace_stbit("state_stale",hdw->state_stale);
+ return state_updated;
+}
+
+
+static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
+ char *buf,unsigned int acnt)
+{
+ switch (which) {
+ case 0:
+ return scnprintf(
+ buf,acnt,
+ "driver:%s%s%s%s%s",
+ (hdw->flag_ok ? " <ok>" : " <fail>"),
+ (hdw->flag_init_ok ? " <init>" : " <uninitialized>"),
+ (hdw->flag_disconnected ? " <disconnected>" :
+ " <connected>"),
+ (hdw->flag_tripped ? " <tripped>" : ""),
+ (hdw->flag_decoder_missed ? " <no decoder>" : ""));
+ case 1:
+ return scnprintf(
+ buf,acnt,
+ "pipeline:%s%s%s%s",
+ (hdw->state_pipeline_idle ? " <idle>" : ""),
+ (hdw->state_pipeline_config ?
+ " <configok>" : " <stale>"),
+ (hdw->state_pipeline_req ? " <req>" : ""),
+ (hdw->state_pipeline_pause ? " <pause>" : ""));
+ case 2:
+ return scnprintf(
+ buf,acnt,
+ "worker:%s%s%s%s%s%s",
+ (hdw->state_decoder_run ?
+ " <decode:run>" :
+ (hdw->state_decoder_quiescent ?
+ "" : " <decode:stop>")),
+ (hdw->state_decoder_quiescent ?
+ " <decode:quiescent>" : ""),
+ (hdw->state_encoder_ok ?
+ "" : " <encode:init>"),
+ (hdw->state_encoder_run ?
+ " <encode:run>" : " <encode:stop>"),
+ (hdw->state_encoder_config ?
+ " <encode:configok>" :
+ (hdw->state_encoder_waitok ?
+ "" : " <encode:wait>")),
+ (hdw->state_usbstream_run ?
+ " <usb:run>" : " <usb:stop>"));
+ break;
+ case 3:
+ return scnprintf(
+ buf,acnt,
+ "state: %s",
+ pvr2_get_state_name(hdw->master_state));
+ break;
+ default: break;
+ }
+ return 0;
+}
+
+
+unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
+ char *buf,unsigned int acnt)
+{
+ unsigned int bcnt,ccnt,idx;
+ bcnt = 0;
+ LOCK_TAKE(hdw->big_lock);
+ for (idx = 0; ; idx++) {
+ ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,acnt);
+ if (!ccnt) break;
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ if (!acnt) break;
+ buf[0] = '\n'; ccnt = 1;
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ }
+ LOCK_GIVE(hdw->big_lock);
+ return bcnt;
+}
+
+
+static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw)
+{
+ char buf[128];
+ unsigned int idx,ccnt;
+
+ for (idx = 0; ; idx++) {
+ ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf));
+ if (!ccnt) break;
+ printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf);
+ }
+}
+
+
+/* Evaluate and update the driver's current state, taking various actions
+ as appropriate for the update. */
+static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw)
+{
+ unsigned int st;
+ int state_updated = 0;
+ int callback_flag = 0;
+
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "Drive state check START");
+ if (pvrusb2_debug & PVR2_TRACE_STBITS) {
+ pvr2_hdw_state_log_state(hdw);
+ }
+
+ /* Process all state and get back over disposition */
+ state_updated = pvr2_hdw_state_update(hdw);
+
+ /* Update master state based upon all other states. */
+ if (!hdw->flag_ok) {
+ st = PVR2_STATE_DEAD;
+ } else if (hdw->fw1_state != FW1_STATE_OK) {
+ st = PVR2_STATE_COLD;
+ } else if (!hdw->state_encoder_ok) {
+ st = PVR2_STATE_WARM;
+ } else if (hdw->flag_tripped || hdw->flag_decoder_missed) {
+ st = PVR2_STATE_ERROR;
+ } else if (hdw->state_encoder_run &&
+ hdw->state_decoder_run &&
+ hdw->state_usbstream_run) {
+ st = PVR2_STATE_RUN;
+ } else {
+ st = PVR2_STATE_READY;
+ }
+ if (hdw->master_state != st) {
+ pvr2_trace(PVR2_TRACE_STATE,
+ "Device state change from %s to %s",
+ pvr2_get_state_name(hdw->master_state),
+ pvr2_get_state_name(st));
+ hdw->master_state = st;
+ state_updated = !0;
+ callback_flag = !0;
+ }
+ if (state_updated) {
+ /* Trigger anyone waiting on any state changes here. */
+ wake_up(&hdw->state_wait_data);
+ }
+
+ if (pvrusb2_debug & PVR2_TRACE_STBITS) {
+ pvr2_hdw_state_log_state(hdw);
+ }
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "Drive state check DONE callback=%d",callback_flag);
+
+ return callback_flag;
+}
+
+
+/* Cause kernel thread to check / update driver state */
+static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_stale) return;
+ hdw->state_stale = !0;
+ trace_stbit("state_stale",hdw->state_stale);
+ queue_work(hdw->workqueue,&hdw->workpoll);
+}
+
+
+void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *ptr)
{
ptr->big_lock_held = hdw->big_lock_held;
ptr->ctl_lock_held = hdw->ctl_lock_held;
- ptr->flag_ok = hdw->flag_ok;
ptr->flag_disconnected = hdw->flag_disconnected;
ptr->flag_init_ok = hdw->flag_init_ok;
- ptr->flag_streaming_enabled = hdw->flag_streaming_enabled;
- ptr->subsys_flags = hdw->subsys_enabled_mask;
+ ptr->flag_ok = hdw->flag_ok;
+ ptr->fw1_state = hdw->fw1_state;
+ ptr->flag_decoder_missed = hdw->flag_decoder_missed;
+ ptr->flag_tripped = hdw->flag_tripped;
+ ptr->state_encoder_ok = hdw->state_encoder_ok;
+ ptr->state_encoder_run = hdw->state_encoder_run;
+ ptr->state_decoder_run = hdw->state_decoder_run;
+ ptr->state_usbstream_run = hdw->state_usbstream_run;
+ ptr->state_decoder_quiescent = hdw->state_decoder_quiescent;
+ ptr->state_pipeline_config = hdw->state_pipeline_config;
+ ptr->state_pipeline_req = hdw->state_pipeline_req;
+ ptr->state_pipeline_pause = hdw->state_pipeline_pause;
+ ptr->state_pipeline_idle = hdw->state_pipeline_idle;
ptr->cmd_debug_state = hdw->cmd_debug_state;
ptr->cmd_code = hdw->cmd_debug_code;
ptr->cmd_debug_write_len = hdw->cmd_debug_write_len;
@@ -3381,6 +3689,15 @@ void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
}
+void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *ptr)
+{
+ LOCK_TAKE(hdw->ctl_lock); do {
+ pvr2_hdw_get_debug_info_unlocked(hdw,ptr);
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+}
+
+
int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp)
{
return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index e2f9d5e4cb65..3ad7a13d6c39 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -44,27 +44,6 @@
#define PVR2_CVAL_INPUT_COMPOSITE 2
#define PVR2_CVAL_INPUT_RADIO 3
-/* Subsystem definitions - these are various pieces that can be
- independently stopped / started. Usually you don't want to mess with
- this directly (let the driver handle things itself), but it is useful
- for debugging. */
-#define PVR2_SUBSYS_B_ENC_FIRMWARE 0
-#define PVR2_SUBSYS_B_ENC_CFG 1
-#define PVR2_SUBSYS_B_DIGITIZER_RUN 2
-#define PVR2_SUBSYS_B_USBSTREAM_RUN 3
-#define PVR2_SUBSYS_B_ENC_RUN 4
-
-#define PVR2_SUBSYS_CFG_ALL ( \
- (1 << PVR2_SUBSYS_B_ENC_FIRMWARE) | \
- (1 << PVR2_SUBSYS_B_ENC_CFG) )
-#define PVR2_SUBSYS_RUN_ALL ( \
- (1 << PVR2_SUBSYS_B_DIGITIZER_RUN) | \
- (1 << PVR2_SUBSYS_B_USBSTREAM_RUN) | \
- (1 << PVR2_SUBSYS_B_ENC_RUN) )
-#define PVR2_SUBSYS_ALL ( \
- PVR2_SUBSYS_CFG_ALL | \
- PVR2_SUBSYS_RUN_ALL )
-
enum pvr2_config {
pvr2_config_empty, /* No configuration */
pvr2_config_mpeg, /* Encoded / compressed video */
@@ -79,8 +58,41 @@ enum pvr2_v4l_type {
pvr2_v4l_type_radio,
};
+/* Major states that we can be in:
+ *
+ * DEAD - Device is in an unusable state and cannot be recovered. This
+ * can happen if we completely lose the ability to communicate with it
+ * (but it might still on the bus). In this state there's nothing we can
+ * do; it must be replugged in order to recover.
+ *
+ * COLD - Device is in an unusuable state, needs microcontroller firmware.
+ *
+ * WARM - We can communicate with the device and the proper
+ * microcontroller firmware is running, but other device initialization is
+ * still needed (e.g. encoder firmware).
+ *
+ * ERROR - A problem prevents capture operation (e.g. encoder firmware
+ * missing).
+ *
+ * READY - Device is operational, but not streaming.
+ *
+ * RUN - Device is streaming.
+ *
+ */
+#define PVR2_STATE_NONE 0
+#define PVR2_STATE_DEAD 1
+#define PVR2_STATE_COLD 2
+#define PVR2_STATE_WARM 3
+#define PVR2_STATE_ERROR 4
+#define PVR2_STATE_READY 5
+#define PVR2_STATE_RUN 6
+
+/* Translate configuration enum to a string label */
const char *pvr2_config_get_name(enum pvr2_config);
+/* Translate a master state enum to a string label */
+const char *pvr2_hdw_get_state_name(unsigned int);
+
struct pvr2_hdw;
/* Create and return a structure for interacting with the underlying
@@ -88,28 +100,13 @@ struct pvr2_hdw;
struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
const struct usb_device_id *devid);
-/* Poll for background activity (if any) */
-void pvr2_hdw_poll(struct pvr2_hdw *);
-
-/* Trigger a poll to take place later at a convenient time */
-void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *);
-
-/* Register a callback used to trigger a future poll */
-void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *,
- void (*func)(void *),
- void *data);
-
/* Destroy hardware interaction structure */
void pvr2_hdw_destroy(struct pvr2_hdw *);
-/* Set up the structure and attempt to put the device into a usable state.
- This can be a time-consuming operation, which is why it is not done
- internally as part of the create() step. Return value is exactly the
- same as pvr2_hdw_init_ok(). */
-int pvr2_hdw_setup(struct pvr2_hdw *);
-
-/* Initialization succeeded */
-int pvr2_hdw_init_ok(struct pvr2_hdw *);
+/* Register a function to be called whenever the master state changes. */
+void pvr2_hdw_set_state_callback(struct pvr2_hdw *,
+ void (*callback_func)(void *),
+ void *callback_data);
/* Return true if in the ready (normal) state */
int pvr2_hdw_dev_ok(struct pvr2_hdw *);
@@ -161,12 +158,21 @@ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *);
/* Query device and see if it thinks it is on a high-speed USB link */
int pvr2_hdw_is_hsm(struct pvr2_hdw *);
+/* Return a string token representative of the hardware type */
+const char *pvr2_hdw_get_type(struct pvr2_hdw *);
+
+/* Return a single line description of the hardware type */
+const char *pvr2_hdw_get_desc(struct pvr2_hdw *);
+
/* Turn streaming on/off */
int pvr2_hdw_set_streaming(struct pvr2_hdw *,int);
/* Find out if streaming is on */
int pvr2_hdw_get_streaming(struct pvr2_hdw *);
+/* Retrieve driver overall state */
+int pvr2_hdw_get_state(struct pvr2_hdw *);
+
/* Configure the type of stream to generate */
int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config);
@@ -177,26 +183,6 @@ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *);
int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std,
unsigned int idx);
-/* Enable / disable various pieces of hardware. Items to change are
- identified by bit positions within msk, and new state for each item is
- identified by corresponding bit positions within val. */
-void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw,
- unsigned long msk,unsigned long val);
-
-/* Retrieve mask indicating which pieces of hardware are currently enabled
- / configured. */
-unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *);
-
-/* Adjust mask of what get shut down when streaming is stopped. This is a
- debugging aid. */
-void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw,
- unsigned long msk,unsigned long val);
-
-/* Retrieve mask indicating which pieces of hardware are disabled when
- streaming is turned off. */
-unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *);
-
-
/* Enable / disable retrieval of CPU firmware or prom contents. This must
be enabled before pvr2_hdw_cpufw_get() will function. Note that doing
this may prevent the device from running (and leaving this mode may
@@ -253,6 +239,9 @@ void pvr2_hdw_cpureset_assert(struct pvr2_hdw *,int);
/* Execute a USB-commanded device reset */
void pvr2_hdw_device_reset(struct pvr2_hdw *);
+/* Reset worker's error trapping circuit breaker */
+int pvr2_hdw_untrip(struct pvr2_hdw *);
+
/* Execute hard reset command (after this point it's likely that the
encoder will have to be reconfigured). This also clears the "useless"
state. */
@@ -275,11 +264,21 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val);
struct pvr2_hdw_debug_info {
int big_lock_held;
int ctl_lock_held;
- int flag_ok;
int flag_disconnected;
int flag_init_ok;
- int flag_streaming_enabled;
- unsigned long subsys_flags;
+ int flag_ok;
+ int fw1_state;
+ int flag_decoder_missed;
+ int flag_tripped;
+ int state_encoder_ok;
+ int state_encoder_run;
+ int state_decoder_run;
+ int state_usbstream_run;
+ int state_decoder_quiescent;
+ int state_pipeline_config;
+ int state_pipeline_req;
+ int state_pipeline_pause;
+ int state_pipeline_idle;
int cmd_debug_state;
int cmd_debug_write_len;
int cmd_debug_read_len;
@@ -295,8 +294,20 @@ struct pvr2_hdw_debug_info {
diagnosing lockups. Note that this operation is completed without any
kind of locking and so it is not atomic and may yield inconsistent
results. This is *purely* a debugging aid. */
-void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
- struct pvr2_hdw_debug_info *);
+void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *);
+
+/* Intrusively retrieve internal state info - this is useful for
+ diagnosing overall driver state. This operation synchronizes against
+ the overall driver mutex - so if there are locking problems this will
+ likely hang! This is *purely* a debugging aid. */
+void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *);
+
+/* Report out several lines of text that describes driver internal state.
+ Results are written into the passed-in buffer. */
+unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
+ char *buf_ptr,unsigned int buf_size);
/* Cause modules to log their state once */
void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw);
@@ -306,9 +317,6 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw);
a debugging aid. */
int pvr2_upload_firmware2(struct pvr2_hdw *hdw);
-/* List of device types that we can match */
-extern struct usb_device_id pvr2_device_table[];
-
#endif /* __PVRUSB2_HDW_H */
/*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
index c817c864e6a0..62867fa3517a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -895,7 +895,7 @@ static int pvr2_i2c_attach_inform(struct i2c_client *client)
list_add_tail(&cp->list,&hdw->i2c_clients);
hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT;
} while (0); mutex_unlock(&hdw->i2c_list_lock);
- if (fl) pvr2_hdw_poll_trigger_unlocked(hdw);
+ if (fl) queue_work(hdw->workqueue,&hdw->worki2csync);
return 0;
}
@@ -980,14 +980,16 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw)
printk(KERN_INFO "%s: IR disabled\n",hdw->name);
hdw->i2c_func[0x18] = i2c_black_hole;
} else if (ir_mode[hdw->unit_number] == 1) {
- if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+ if (hdw->hdw_desc->flag_has_hauppauge_custom_ir) {
hdw->i2c_func[0x18] = i2c_24xxx_ir;
}
}
- if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
- hdw->i2c_func[0x1b] = i2c_hack_wm8775;
+ if (hdw->hdw_desc->flag_has_cx25840) {
hdw->i2c_func[0x44] = i2c_hack_cx25840;
}
+ if (hdw->hdw_desc->flag_has_wm8775) {
+ hdw->i2c_func[0x1b] = i2c_hack_wm8775;
+ }
// Configure the adapter and set up everything else related to it.
memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap));
diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c
index 11b3b2e84b90..b63b2265503a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-main.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-main.c
@@ -28,6 +28,7 @@
#include <linux/videodev2.h>
#include "pvrusb2-hdw.h"
+#include "pvrusb2-devattr.h"
#include "pvrusb2-context.h"
#include "pvrusb2-debug.h"
#include "pvrusb2-v4l2.h"
@@ -148,11 +149,6 @@ static void __exit pvr_exit(void)
module_init(pvr_init);
module_exit(pvr_exit);
-/* Mike Isely <mcisely@pobox.com> 11-Mar-2006: See pvrusb2-hdw.c for
- MODULE_DEVICE_TABLE(). We have to declare that attribute there
- because that's where the device table actually is now and it seems
- that certain gcc configurations get angry if MODULE_DEVICE_TABLE()
- is used on what ends up being an external symbol. */
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/video/pvrusb2/pvrusb2-std.c
index 63e55bb59fcb..da309288daa4 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-std.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-std.c
@@ -50,6 +50,10 @@ struct std_name {
V4L2_STD_NTSC_M_KR| \
V4L2_STD_NTSC_443)
+#define CSTD_ATSC \
+ (V4L2_STD_ATSC_8_VSB| \
+ V4L2_STD_ATSC_16_VSB)
+
#define CSTD_SECAM \
(V4L2_STD_SECAM_B| \
V4L2_STD_SECAM_D| \
@@ -82,6 +86,7 @@ static const struct std_name std_groups[] = {
{"PAL",CSTD_PAL},
{"NTSC",CSTD_NTSC},
{"SECAM",CSTD_SECAM},
+ {"ATSC",CSTD_ATSC},
};
/* Mapping of standard bits to modulation system */
@@ -104,6 +109,8 @@ static const struct std_name std_items[] = {
{"N",TSTD_N},
{"Nc",TSTD_Nc},
{"60",TSTD_60},
+ {"8VSB",V4L2_STD_ATSC_8_VSB},
+ {"16VSB",V4L2_STD_ATSC_16_VSB},
};
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index 3c57a7d8200b..7a1cd878e31a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -43,10 +43,14 @@ struct pvr2_sysfs {
struct device_attribute attr_v4l_radio_minor_number;
struct device_attribute attr_unit_number;
struct device_attribute attr_bus_info;
+ struct device_attribute attr_hdw_name;
+ struct device_attribute attr_hdw_desc;
int v4l_minor_number_created_ok;
int v4l_radio_minor_number_created_ok;
int unit_number_created_ok;
int bus_info_created_ok;
+ int hdw_name_created_ok;
+ int hdw_desc_created_ok;
};
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
@@ -712,6 +716,14 @@ static void class_dev_destroy(struct pvr2_sysfs *sfp)
pvr2_sysfs_tear_down_debugifc(sfp);
#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
pvr2_sysfs_tear_down_controls(sfp);
+ if (sfp->hdw_desc_created_ok) {
+ device_remove_file(sfp->class_dev,
+ &sfp->attr_hdw_desc);
+ }
+ if (sfp->hdw_name_created_ok) {
+ device_remove_file(sfp->class_dev,
+ &sfp->attr_hdw_name);
+ }
if (sfp->bus_info_created_ok) {
device_remove_file(sfp->class_dev,
&sfp->attr_bus_info);
@@ -758,6 +770,28 @@ static ssize_t bus_info_show(struct device *class_dev,
}
+static ssize_t hdw_name_show(struct device *class_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->driver_data;
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%s\n",
+ pvr2_hdw_get_type(sfp->channel.hdw));
+}
+
+
+static ssize_t hdw_desc_show(struct device *class_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->driver_data;
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%s\n",
+ pvr2_hdw_get_desc(sfp->channel.hdw));
+}
+
+
static ssize_t v4l_radio_minor_number_show(struct device *class_dev,
struct device_attribute *attr,
char *buf)
@@ -871,6 +905,32 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
sfp->bus_info_created_ok = !0;
}
+ sfp->attr_hdw_name.attr.name = "device_hardware_type";
+ sfp->attr_hdw_name.attr.mode = S_IRUGO;
+ sfp->attr_hdw_name.show = hdw_name_show;
+ sfp->attr_hdw_name.store = NULL;
+ ret = device_create_file(sfp->class_dev,
+ &sfp->attr_hdw_name);
+ if (ret < 0) {
+ printk(KERN_WARNING "%s: device_create_file error: %d\n",
+ __FUNCTION__, ret);
+ } else {
+ sfp->hdw_name_created_ok = !0;
+ }
+
+ sfp->attr_hdw_desc.attr.name = "device_hardware_description";
+ sfp->attr_hdw_desc.attr.mode = S_IRUGO;
+ sfp->attr_hdw_desc.show = hdw_desc_show;
+ sfp->attr_hdw_desc.store = NULL;
+ ret = device_create_file(sfp->class_dev,
+ &sfp->attr_hdw_desc);
+ if (ret < 0) {
+ printk(KERN_WARNING "%s: device_create_file error: %d\n",
+ __FUNCTION__, ret);
+ } else {
+ sfp->hdw_desc_created_ok = !0;
+ }
+
pvr2_sysfs_add_controls(sfp);
#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
pvr2_sysfs_add_debugifc(sfp);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 7a596ea7cfe6..8f0587ebd4bd 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -205,6 +205,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw),
sizeof(cap->bus_info));
+ strlcpy(cap->card,pvr2_hdw_get_desc(hdw),sizeof(cap->card));
ret = 0;
break;
@@ -1015,10 +1016,8 @@ static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh)
sp = fh->dev_info->stream->stream;
pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh);
pvr2_hdw_set_stream_type(hdw,fh->dev_info->config);
- pvr2_hdw_set_streaming(hdw,!0);
- ret = pvr2_ioread_set_enabled(fh->rhp,!0);
-
- return ret;
+ if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret;
+ return pvr2_ioread_set_enabled(fh->rhp,!0);
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
index 61efa6f02200..7c47345501b6 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
@@ -49,29 +49,50 @@ struct pvr2_v4l_decoder {
};
+struct routing_scheme {
+ const int *def;
+ unsigned int cnt;
+};
+
+
+static const int routing_scheme0[] = {
+ [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4,
+ /* In radio mode, we mute the video, but point at one
+ spot just to stay consistent */
+ [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5,
+ [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE5,
+ [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2,
+};
+
+static const struct routing_scheme routing_schemes[] = {
+ [PVR2_ROUTING_SCHEME_HAUPPAUGE] = {
+ .def = routing_scheme0,
+ .cnt = ARRAY_SIZE(routing_scheme0),
+ },
+};
+
static void set_input(struct pvr2_v4l_decoder *ctxt)
{
struct pvr2_hdw *hdw = ctxt->hdw;
struct v4l2_routing route;
+ const struct routing_scheme *sp;
+ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val);
- switch(hdw->input_val) {
- case PVR2_CVAL_INPUT_TV:
- route.input = SAA7115_COMPOSITE4;
- break;
- case PVR2_CVAL_INPUT_COMPOSITE:
- route.input = SAA7115_COMPOSITE5;
- break;
- case PVR2_CVAL_INPUT_SVIDEO:
- route.input = SAA7115_SVIDEO2;
- break;
- case PVR2_CVAL_INPUT_RADIO:
- // In radio mode, we mute the video, but point at one
- // spot just to stay consistent
- route.input = SAA7115_COMPOSITE5;
- default:
+
+ if ((sid < ARRAY_SIZE(routing_schemes)) &&
+ ((sp = routing_schemes + sid) != 0) &&
+ (hdw->input_val >= 0) &&
+ (hdw->input_val < sp->cnt)) {
+ route.input = sp->def[hdw->input_val];
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "*** WARNING *** i2c v4l2 set_input:"
+ " Invalid routing scheme (%u) and/or input (%d)",
+ sid,hdw->input_val);
return;
}
+
route.output = 0;
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route);
}
@@ -129,7 +150,7 @@ static const struct pvr2_v4l_decoder_ops decoder_ops[] = {
static void decoder_detach(struct pvr2_v4l_decoder *ctxt)
{
ctxt->client->handler = NULL;
- ctxt->hdw->decoder_ctrl = NULL;
+ pvr2_hdw_set_decoder(ctxt->hdw,NULL);
kfree(ctxt);
}
@@ -217,7 +238,7 @@ int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw,
ctxt->client = cp;
ctxt->hdw = hdw;
ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1;
- hdw->decoder_ctrl = &ctxt->ctrl;
+ pvr2_hdw_set_decoder(hdw,&ctxt->ctrl);
cp->handler = &ctxt->handler;
pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up",
cp->client->addr);
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index 2d18f0069821..41e5e518a47e 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -46,6 +46,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include <media/saa7115.h>
#include <asm/div64.h>
@@ -1230,7 +1231,7 @@ static void saa711x_decode_vbi_line(struct i2c_client *client,
/* ============ SAA7115 AUDIO settings (end) ============= */
-static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
struct saa711x_state *state = i2c_get_clientdata(client);
@@ -1449,26 +1450,17 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar
/* ----------------------------------------------------------------------- */
-static struct i2c_driver i2c_driver_saa711x;
-
-static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind)
+static int saa7115_probe(struct i2c_client *client)
{
- struct i2c_client *client;
struct saa711x_state *state;
int i;
char name[17];
u8 chip_id;
/* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return 0;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == 0)
- return -ENOMEM;
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver_saa711x;
snprintf(client->name, sizeof(client->name) - 1, "saa7115");
for (i = 0; i < 0x0f; i++) {
@@ -1485,18 +1477,16 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind)
/* Check whether this chip is part of the saa711x series */
if (memcmp(name, "1f711", 5)) {
v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n",
- address << 1, name);
- kfree(client);
- return 0;
+ client->addr << 1, name);
+ 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, address << 1, adapter->name);
+ v4l_info(client, "saa711%d 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);
if (state == NULL) {
- kfree(client);
return -ENOMEM;
}
state->input = -1;
@@ -1549,59 +1539,25 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind)
saa711x_writeregs(client, saa7115_init_misc);
saa711x_set_v4lstd(client, V4L2_STD_NTSC);
- i2c_attach_client(client);
-
v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n",
saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC), saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC));
-
return 0;
}
-static int saa711x_probe(struct i2c_adapter *adapter)
-{
- if (adapter->class & I2C_CLASS_TV_ANALOG || adapter->class & I2C_CLASS_TV_DIGITAL)
- return i2c_probe(adapter, &addr_data, &saa711x_attach);
- return 0;
-}
+/* ----------------------------------------------------------------------- */
-static int saa711x_detach(struct i2c_client *client)
+static int saa7115_remove(struct i2c_client *client)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
- int err;
-
- err = i2c_detach_client(client);
- if (err) {
- return err;
- }
-
- kfree(state);
- kfree(client);
+ kfree(i2c_get_clientdata(client));
return 0;
}
-/* ----------------------------------------------------------------------- */
-
-/* i2c implementation */
-static struct i2c_driver i2c_driver_saa711x = {
- .driver = {
- .name = "saa7115",
- },
- .id = I2C_DRIVERID_SAA711X,
- .attach_adapter = saa711x_probe,
- .detach_client = saa711x_detach,
- .command = saa711x_command,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa7115",
+ .driverid = I2C_DRIVERID_SAA711X,
+ .command = saa7115_command,
+ .probe = saa7115_probe,
+ .remove = saa7115_remove,
+ .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
};
-
-static int __init saa711x_init_module(void)
-{
- return i2c_add_driver(&i2c_driver_saa711x);
-}
-
-static void __exit saa711x_cleanup_module(void)
-{
- i2c_del_driver(&i2c_driver_saa711x);
-}
-
-module_init(saa711x_init_module);
-module_exit(saa711x_cleanup_module);
diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
index e35ef321ec71..06c88db656b4 100644
--- a/drivers/media/video/saa7127.c
+++ b/drivers/media/video/saa7127.c
@@ -55,10 +55,11 @@
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv.h>
#include <media/saa7127.h>
-static int debug = 0;
-static int test_image = 0;
+static int debug;
+static int test_image;
MODULE_DESCRIPTION("Philips SAA7127/9 video encoder driver");
MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
@@ -68,10 +69,6 @@ module_param(test_image, int, 0644);
MODULE_PARM_DESC(debug, "debug level (0-2)");
MODULE_PARM_DESC(test_image, "test_image (0-1)");
-static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END };
-
-
-I2C_CLIENT_INSMOD;
/*
* SAA7127 registers
@@ -360,9 +357,10 @@ static int saa7127_set_cc(struct i2c_client *client, struct v4l2_sliced_vbi_data
if (enable && (data->field != 0 || data->line != 21))
return -EINVAL;
if (state->cc_enable != enable) {
- v4l_dbg(1, debug, client, "Turn CC %s\n", enable ? "on" : "off");
+ v4l_dbg(1, debug, client,
+ "Turn CC %s\n", enable ? "on" : "off");
saa7127_write(client, SAA7127_REG_CLOSED_CAPTION,
- (state->xds_enable << 7) | (enable << 6) | 0x11);
+ (state->xds_enable << 7) | (enable << 6) | 0x11);
state->cc_enable = enable;
}
if (!enable)
@@ -420,7 +418,8 @@ static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_dat
saa7127_write(client, 0x26, data->data[0]);
saa7127_write(client, 0x27, 0x80 | (data->data[1] & 0x3f));
- v4l_dbg(1, debug, client, "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]);
+ v4l_dbg(1, debug, client,
+ "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]);
state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0];
return 0;
}
@@ -507,7 +506,8 @@ static int saa7127_set_output_type(struct i2c_client *client, int output)
default:
return -EINVAL;
}
- v4l_dbg(1, debug, client, "Selecting %s output type\n", output_strs[output]);
+ v4l_dbg(1, debug, client,
+ "Selecting %s output type\n", output_strs[output]);
/* Configure Encoder */
saa7127_write(client, 0x2d, state->reg_2d);
@@ -569,12 +569,10 @@ static int saa7127_command(struct i2c_client *client,
{
int rc = 0;
- if (state->input_type != route->input) {
+ if (state->input_type != route->input)
rc = saa7127_set_input_type(client, route->input);
- }
- if (rc == 0 && state->output_type != route->output) {
+ if (rc == 0 && state->output_type != route->output)
rc = saa7127_set_output_type(client, route->output);
- }
return rc;
}
@@ -620,7 +618,8 @@ static int saa7127_command(struct i2c_client *client,
{
struct v4l2_register *reg = arg;
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@@ -637,16 +636,16 @@ static int saa7127_command(struct i2c_client *client,
struct v4l2_sliced_vbi_data *data = arg;
switch (data->id) {
- case V4L2_SLICED_WSS_625:
- return saa7127_set_wss(client, data);
- case V4L2_SLICED_VPS:
- return saa7127_set_vps(client, data);
- case V4L2_SLICED_CAPTION_525:
- if (data->field == 0)
- return saa7127_set_cc(client, data);
- return saa7127_set_xds(client, data);
- default:
- return -EINVAL;
+ case V4L2_SLICED_WSS_625:
+ return saa7127_set_wss(client, data);
+ case V4L2_SLICED_VPS:
+ return saa7127_set_vps(client, data);
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0)
+ return saa7127_set_cc(client, data);
+ return saa7127_set_xds(client, data);
+ default:
+ return -EINVAL;
}
break;
}
@@ -662,31 +661,20 @@ static int saa7127_command(struct i2c_client *client,
/* ----------------------------------------------------------------------- */
-static struct i2c_driver i2c_driver_saa7127;
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_attach(struct i2c_adapter *adapter, int address, int kind)
+static int saa7127_probe(struct i2c_client *client)
{
- struct i2c_client *client;
struct saa7127_state *state;
struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */
int read_result = 0;
/* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return 0;
-
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == 0)
- return -ENOMEM;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver_saa7127;
snprintf(client->name, sizeof(client->name) - 1, "saa7127");
- v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n", address << 1);
+ v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n",
+ client->addr << 1);
/* First test register 0: Bits 5-7 are a version ID (should be 0),
and bit 2 should also be 0.
@@ -696,15 +684,12 @@ static int saa7127_attach(struct i2c_adapter *adapter, int address, int kind)
if ((saa7127_read(client, 0) & 0xe4) != 0 ||
(saa7127_read(client, 0x29) & 0x3f) != 0x1d) {
v4l_dbg(1, debug, client, "saa7127 not found\n");
- kfree(client);
- return 0;
+ return -ENODEV;
}
state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL);
- if (state == NULL) {
- kfree(client);
- return (-ENOMEM);
- }
+ if (state == NULL)
+ return -ENOMEM;
i2c_set_clientdata(client, state);
@@ -718,91 +703,48 @@ static int saa7127_attach(struct i2c_adapter *adapter, int address, int kind)
saa7127_set_wss(client, &vbi);
saa7127_set_cc(client, &vbi);
saa7127_set_xds(client, &vbi);
- if (test_image == 1) {
+ if (test_image == 1)
/* The Encoder has an internal Colorbar generator */
/* This can be used for debugging */
saa7127_set_input_type(client, SAA7127_INPUT_TYPE_TEST_IMAGE);
- } else {
+ else
saa7127_set_input_type(client, SAA7127_INPUT_TYPE_NORMAL);
- }
saa7127_set_video_enable(client, 1);
/* Detect if it's an saa7129 */
read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2);
saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa);
if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
- v4l_info(client, "saa7129 found @ 0x%x (%s)\n", address << 1, adapter->name);
+ v4l_info(client, "saa7129 found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, read_result);
saa7127_write_inittab(client, saa7129_init_config_extra);
state->ident = V4L2_IDENT_SAA7129;
} else {
- v4l_info(client, "saa7127 found @ 0x%x (%s)\n", address << 1, adapter->name);
+ v4l_info(client, "saa7127 found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
state->ident = V4L2_IDENT_SAA7127;
}
-
- i2c_attach_client(client);
-
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_probe(struct i2c_adapter *adapter)
+static int saa7127_remove(struct i2c_client *client)
{
- if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, saa7127_attach);
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_detach(struct i2c_client *client)
-{
- struct saa7127_state *state = i2c_get_clientdata(client);
- int err;
-
/* Turn off TV output */
saa7127_set_video_enable(client, 0);
-
- err = i2c_detach_client(client);
-
- if (err) {
- return err;
- }
-
- kfree(state);
- kfree(client);
+ kfree(i2c_get_clientdata(client));
return 0;
}
/* ----------------------------------------------------------------------- */
-static struct i2c_driver i2c_driver_saa7127 = {
- .driver = {
- .name = "saa7127",
- },
- .id = I2C_DRIVERID_SAA7127,
- .attach_adapter = saa7127_probe,
- .detach_client = saa7127_detach,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa7127",
+ .driverid = I2C_DRIVERID_SAA7127,
.command = saa7127_command,
+ .probe = saa7127_probe,
+ .remove = saa7127_remove,
};
-
-/* ----------------------------------------------------------------------- */
-
-static int __init saa7127_init_module(void)
-{
- return i2c_add_driver(&i2c_driver_saa7127);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static void __exit saa7127_cleanup_module(void)
-{
- i2c_del_driver(&i2c_driver_saa7127);
-}
-
-/* ----------------------------------------------------------------------- */
-
-module_init(saa7127_init_module);
-module_exit(saa7127_cleanup_module);
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
index 3aa8cb2b860a..96bc3b1298a2 100644
--- a/drivers/media/video/saa7134/Kconfig
+++ b/drivers/media/video/saa7134/Kconfig
@@ -4,6 +4,7 @@ config VIDEO_SAA7134
select VIDEOBUF_DMA_SG
select VIDEO_IR
select VIDEO_TUNER
+ select VIDEO_TVEEPROM
select CRC32
---help---
This is a video4linux driver for Philips SAA713x based
@@ -23,18 +24,6 @@ config VIDEO_SAA7134_ALSA
To compile this driver as a module, choose M here: the
module will be called saa7134-alsa.
-config VIDEO_SAA7134_OSS
- tristate "Philips SAA7134 DMA audio support (OSS, DEPRECATED)"
- depends on VIDEO_SAA7134 && SOUND_PRIME && !VIDEO_SAA7134_ALSA
- ---help---
- This is a video4linux driver for direct (DMA) audio in
- Philips SAA713x based TV cards using OSS
-
- This is deprecated in favor of the ALSA module
-
- To compile this driver as a module, choose M here: the
- module will be called saa7134-oss.
-
config VIDEO_SAA7134_DVB
tristate "DVB/ATSC Support for saa7134 based TV cards"
depends on VIDEO_SAA7134 && DVB_CORE
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
index c85c8a8ec361..9aff937ba7a5 100644
--- a/drivers/media/video/saa7134/Makefile
+++ b/drivers/media/video/saa7134/Makefile
@@ -7,7 +7,6 @@ obj-$(CONFIG_VIDEO_SAA7134) += saa7134.o saa7134-empress.o \
saa6752hs.o
obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o
-obj-$(CONFIG_VIDEO_SAA7134_OSS) += saa7134-oss.o
obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c
index 4878f3067787..ba2531034a91 100644
--- a/drivers/media/video/saa7134/saa7134-alsa.c
+++ b/drivers/media/video/saa7134/saa7134-alsa.c
@@ -1077,24 +1077,14 @@ static int saa7134_alsa_init(void)
struct saa7134_dev *dev = NULL;
struct list_head *list;
- if (!saa7134_dmasound_init && !saa7134_dmasound_exit) {
- saa7134_dmasound_init = alsa_device_init;
- saa7134_dmasound_exit = alsa_device_exit;
- } else {
- printk(KERN_WARNING "saa7134 ALSA: can't load, DMA sound handler already assigned (probably to OSS)\n");
- return -EBUSY;
- }
+ saa7134_dmasound_init = alsa_device_init;
+ saa7134_dmasound_exit = alsa_device_exit;
printk(KERN_INFO "saa7134 ALSA driver for DMA sound loaded\n");
list_for_each(list,&saa7134_devlist) {
dev = list_entry(list, struct saa7134_dev, devlist);
- if (dev->dmasound.priv_data == NULL) {
- alsa_device_init(dev);
- } else {
- printk(KERN_ERR "saa7134 ALSA: DMA sound is being handled by OSS. ignoring %s\n",dev->name);
- return -EBUSY;
- }
+ alsa_device_init(dev);
}
if (dev == NULL)
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 98c1b084a716..7d7f383b404f 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -26,6 +26,7 @@
#include "saa7134-reg.h"
#include "saa7134.h"
#include <media/v4l2-common.h>
+#include <media/tveeprom.h>
/* commly used strings */
static char name_mute[] = "mute";
@@ -349,6 +350,10 @@ struct saa7134_board saa7134_boards[] = {
.name = name_radio,
.amux = LINE2,
},
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ },
},
[SAA7134_BOARD_TVSTATION_RDS] = {
/* Typhoon TV Tuner RDS: Art.Nr. 50694 */
@@ -565,6 +570,10 @@ struct saa7134_board saa7134_boards[] = {
.radio = {
.name = name_radio,
.amux = LINE2,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
},
},
[SAA7134_BOARD_TYPHOON_90031] = {
@@ -3553,6 +3562,356 @@ struct saa7134_board saa7134_boards[] = {
.tv = 1,
}},
},
+ [SAA7134_BOARD_BEHOLD_401] = {
+ .name = "Beholder BeholdTV 401",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_403] = {
+ .name = "Beholder BeholdTV 403",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_BEHOLD_403FM] = {
+ .name = "Beholder BeholdTV 403 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_405] = {
+ .name = "Beholder BeholdTV 405",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_BEHOLD_405FM] = {
+ /* Sergey <skiv@orel.ru> */
+ .name = "Beholder BeholdTV 405 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_407] = {
+ .name = "Beholder BeholdTV 407",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0xc0c000,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0xc0c000,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0xc0c000,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0xc0c000,
+ }},
+ },
+ [SAA7134_BOARD_BEHOLD_407FM] = {
+ .name = "Beholder BeholdTV 407 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0xc0c000,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0xc0c000,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0xc0c000,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0xc0c000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0xc0c000,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_409] = {
+ .name = "Beholder BeholdTV 409",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_BEHOLD_505FM] = {
+ .name = "Beholder BeholdTV 505 FM/RDS",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_507_9FM] = {
+ .name = "Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM] = {
+ .name = "Beholder BeholdTV Columbus TVFM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ALPS_TSBE5_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_607_9FM] = {
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ .name = "Beholder BeholdTV 607 / BeholdTV 609",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_M6] = {
+ /* Igor Kuznetsov <igk@igk.ru> */
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ .name = "Beholder BeholdTV M6 / BeholdTV M6 Extra",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ },
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -4033,7 +4392,13 @@ struct pci_device_id saa7134_pci_tbl[] = {
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x1462,
- .subdevice = 0x6231,
+ .subdevice = 0x6231, /* tda8275a, ks003 IR */
+ .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1462,
+ .subdevice = 0x8624, /* tda8275, ks003 IR */
.driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
@@ -4207,11 +4572,41 @@ struct pci_device_id saa7134_pci_tbl[] = {
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x0070,
+ .subdevice = 0x6700,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
.subdevice = 0x6701,
.driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6702,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6703,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6704,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6705,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x153b,
.subdevice = 0x1172,
.driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA,
@@ -4289,6 +4684,162 @@ struct pci_device_id saa7134_pci_tbl[] = {
.driver_data = SAA7134_BOARD_AVERMEDIA_SUPER_007,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0000,
+ .subdevice = 0x4016,
+ .driver_data = SAA7134_BOARD_BEHOLD_401,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x0000,
+ .subdevice = 0x4036,
+ .driver_data = SAA7134_BOARD_BEHOLD_403,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x0000,
+ .subdevice = 0x4037,
+ .driver_data = SAA7134_BOARD_BEHOLD_403FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0000,
+ .subdevice = 0x4050,
+ .driver_data = SAA7134_BOARD_BEHOLD_405,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0000,
+ .subdevice = 0x4051,
+ .driver_data = SAA7134_BOARD_BEHOLD_405FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x0000,
+ .subdevice = 0x4070,
+ .driver_data = SAA7134_BOARD_BEHOLD_407,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x0000,
+ .subdevice = 0x4071,
+ .driver_data = SAA7134_BOARD_BEHOLD_407FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0000,
+ .subdevice = 0x4090,
+ .driver_data = SAA7134_BOARD_BEHOLD_409,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0000,
+ .subdevice = 0x5051,
+ .driver_data = SAA7134_BOARD_BEHOLD_505FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0000,
+ .subdevice = 0x505B,
+ .driver_data = SAA7134_BOARD_BEHOLD_505FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x5ace,
+ .subdevice = 0x5050,
+ .driver_data = SAA7134_BOARD_BEHOLD_505FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0000,
+ .subdevice = 0x5071,
+ .driver_data = SAA7134_BOARD_BEHOLD_507_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0000,
+ .subdevice = 0x507B,
+ .driver_data = SAA7134_BOARD_BEHOLD_507_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5ace,
+ .subdevice = 0x5070,
+ .driver_data = SAA7134_BOARD_BEHOLD_507_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x5090,
+ .driver_data = SAA7134_BOARD_BEHOLD_507_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0000,
+ .subdevice = 0x5201,
+ .driver_data = SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6070,
+ .driver_data = SAA7134_BOARD_BEHOLD_607_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6071,
+ .driver_data = SAA7134_BOARD_BEHOLD_607_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6072,
+ .driver_data = SAA7134_BOARD_BEHOLD_607_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6073,
+ .driver_data = SAA7134_BOARD_BEHOLD_607_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6090,
+ .driver_data = SAA7134_BOARD_BEHOLD_607_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6091,
+ .driver_data = SAA7134_BOARD_BEHOLD_607_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6092,
+ .driver_data = SAA7134_BOARD_BEHOLD_607_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6093,
+ .driver_data = SAA7134_BOARD_BEHOLD_607_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6190,
+ .driver_data = SAA7134_BOARD_BEHOLD_M6,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6193,
+ .driver_data = SAA7134_BOARD_BEHOLD_M6,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x4e42,
.subdevice = 0x3502,
@@ -4351,6 +4902,34 @@ static void board_flyvideo(struct saa7134_dev *dev)
/* ----------------------------------------------------------- */
+static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data)
+{
+ struct tveeprom tv;
+
+ tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data);
+
+ /* Make sure we support the board model */
+ switch (tv.model) {
+ case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */
+ case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */
+ case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
+ case 67569: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM) */
+ case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */
+ case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */
+ case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */
+ break;
+ default:
+ printk(KERN_WARNING "%s: warning: "
+ "unknown hauppauge model #%d\n", dev->name, tv.model);
+ break;
+ }
+
+ printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n",
+ dev->name, tv.model);
+}
+
+/* ----------------------------------------------------------- */
+
int saa7134_board_init1(struct saa7134_dev *dev)
{
/* Always print gpio, often manufacturers encode tuner type and other info. */
@@ -4406,6 +4985,16 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_ENCORE_ENLTV:
case SAA7134_BOARD_ENCORE_ENLTV_FM:
case SAA7134_BOARD_10MOONSTVMASTER3:
+ case SAA7134_BOARD_BEHOLD_401:
+ case SAA7134_BOARD_BEHOLD_403:
+ case SAA7134_BOARD_BEHOLD_403FM:
+ case SAA7134_BOARD_BEHOLD_405:
+ case SAA7134_BOARD_BEHOLD_405FM:
+ case SAA7134_BOARD_BEHOLD_407:
+ case SAA7134_BOARD_BEHOLD_407FM:
+ case SAA7134_BOARD_BEHOLD_409:
+ case SAA7134_BOARD_BEHOLD_505FM:
+ case SAA7134_BOARD_BEHOLD_507_9FM:
dev->has_remote = SAA7134_REMOTE_GPIO;
break;
case SAA7134_BOARD_FLYDVBS_LR300:
@@ -4445,6 +5034,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_BEHOLD_COLUMBUS_TVFM:
/* power-up tuner chip */
saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0xffffffff);
saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0xffffffff);
@@ -4466,6 +5056,8 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_PINNACLE_PCTV_310i:
case SAA7134_BOARD_UPMOST_PURPLE_TV:
case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+ case SAA7134_BOARD_BEHOLD_607_9FM:
+ case SAA7134_BOARD_BEHOLD_M6:
dev->has_remote = SAA7134_REMOTE_I2C;
break;
case SAA7134_BOARD_AVERMEDIA_A169_B:
@@ -4477,6 +5069,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_AVERMEDIA_M102:
/* enable tuner */
+ dev->has_remote = SAA7134_REMOTE_GPIO;
saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x8c040007, 0x8c040007);
saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd);
break;
@@ -4570,8 +5163,17 @@ int saa7134_board_init2(struct saa7134_dev *dev)
printk(KERN_INFO "%s Tuner type is %d\n", dev->name, dev->tuner_type);
if (dev->tuner_type == TUNER_PHILIPS_FMD1216ME_MK3) {
- dev->tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE;
- saa7134_i2c_call_clients(dev,TDA9887_SET_CONFIG, &dev->tda9887_conf);
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &dev->tda9887_conf;
+
+ dev->tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE;
+
+ saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG,
+ &tda9887_cfg);
}
tun_setup.mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
@@ -4601,7 +5203,6 @@ int saa7134_board_init2(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_PHILIPS_TIGER:
case SAA7134_BOARD_PHILIPS_TIGER_S:
- case SAA7134_BOARD_AVERMEDIA_SUPER_007:
{
u8 data[] = { 0x3c, 0x33, 0x60};
struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
@@ -4622,13 +5223,16 @@ int saa7134_board_init2(struct saa7134_dev *dev)
i2c_transfer(&dev->i2c_adap, &msg, 1);
}
break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+ hauppauge_eeprom(dev, dev->eedata+0x80);
+ /* break intentionally omitted */
case SAA7134_BOARD_PINNACLE_PCTV_310i:
case SAA7134_BOARD_KWORLD_DVBT_210:
case SAA7134_BOARD_TEVION_DVBT_220RF:
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
case SAA7134_BOARD_MEDION_MD8800_QUADRO:
- case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+ case SAA7134_BOARD_AVERMEDIA_SUPER_007:
/* this is a hybrid board, initialize to analog mode
* and configure firmware eeprom address
*/
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index 4f0a9157ecb1..52baa4f7f7dd 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -294,7 +294,7 @@ void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf)
videobuf_waiton(&buf->vb,0,0);
videobuf_dma_unmap(q, dma);
videobuf_dma_free(dma);
- buf->vb.state = STATE_NEEDS_INIT;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
/* ------------------------------------------------------------------ */
@@ -313,7 +313,7 @@ int saa7134_buffer_queue(struct saa7134_dev *dev,
buf->activate(dev,buf,NULL);
} else if (list_empty(&q->queue)) {
list_add_tail(&buf->vb.queue,&q->queue);
- buf->vb.state = STATE_QUEUED;
+ buf->vb.state = VIDEOBUF_QUEUED;
} else {
next = list_entry(q->queue.next,struct saa7134_buf,
vb.queue);
@@ -322,7 +322,7 @@ int saa7134_buffer_queue(struct saa7134_dev *dev,
}
} else {
list_add_tail(&buf->vb.queue,&q->queue);
- buf->vb.state = STATE_QUEUED;
+ buf->vb.state = VIDEOBUF_QUEUED;
}
return 0;
}
@@ -387,7 +387,7 @@ void saa7134_buffer_timeout(unsigned long data)
try to start over with the next one. */
if (q->curr) {
dprintk("timeout on %p\n",q->curr);
- saa7134_buffer_finish(dev,q,STATE_ERROR);
+ saa7134_buffer_finish(dev,q,VIDEOBUF_ERROR);
}
saa7134_buffer_next(dev,q);
spin_unlock_irqrestore(&dev->slock,flags);
@@ -395,8 +395,8 @@ void saa7134_buffer_timeout(unsigned long data)
/* resends a current buffer in queue after resume */
-int saa7134_buffer_requeue(struct saa7134_dev *dev,
- struct saa7134_dmaqueue *q)
+static int saa7134_buffer_requeue(struct saa7134_dev *dev,
+ struct saa7134_dmaqueue *q)
{
struct saa7134_buf *buf, *next;
@@ -834,6 +834,7 @@ static struct video_device *vdev_init(struct saa7134_dev *dev,
vfd->minor = -1;
vfd->dev = &dev->pci->dev;
vfd->release = video_device_release;
+ vfd->debug = video_debug;
snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
dev->name, type, saa7134_boards[dev->board].name);
return vfd;
@@ -1052,7 +1053,9 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
printk(KERN_INFO "%s: registered device video%d [v4l2]\n",
dev->name,dev->video_dev->minor & 0x1f);
- dev->vbi_dev = vdev_init(dev,&saa7134_vbi_template,"vbi");
+ dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi");
+ dev->vbi_dev->type = VID_TYPE_TUNER | VID_TYPE_TELETEXT;
+
err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
vbi_nr[dev->nr]);
if (err < 0)
@@ -1181,8 +1184,13 @@ static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state)
saa_writel(SAA7134_IRQ2, 0);
saa_writel(SAA7134_MAIN_CTRL, 0);
- synchronize_irq(pci_dev->irq);
dev->insuspend = 1;
+ synchronize_irq(pci_dev->irq);
+
+ /* ACK interrupts once more, just in case,
+ since the IRQ handler won't ack them anymore*/
+
+ saa_writel(SAA7134_IRQ_REPORT, saa_readl(SAA7134_IRQ_REPORT));
/* Disable timeout timers - if we have active buffers, we will
fill them on resume*/
@@ -1194,10 +1202,10 @@ static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state)
if (dev->remote)
saa7134_ir_stop(dev);
- pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
pci_save_state(pci_dev);
+ pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
- return 0;
+ return 0;
}
static int saa7134_resume(struct pci_dev *pci_dev)
@@ -1205,8 +1213,8 @@ static int saa7134_resume(struct pci_dev *pci_dev)
struct saa7134_dev *dev = pci_get_drvdata(pci_dev);
unsigned long flags;
- pci_restore_state(pci_dev);
pci_set_power_state(pci_dev, PCI_D0);
+ pci_restore_state(pci_dev);
/* Do things that are done in saa7134_initdev ,
except of initializing memory structures.*/
@@ -1222,6 +1230,7 @@ static int saa7134_resume(struct pci_dev *pci_dev)
saa7134_ir_start(dev, dev->remote);
saa7134_hw_enable1(dev);
+ msleep(100);
saa7134_board_init2(dev);
@@ -1229,10 +1238,13 @@ static int saa7134_resume(struct pci_dev *pci_dev)
saa7134_set_tvnorm_hw(dev);
saa7134_tvaudio_setmute(dev);
saa7134_tvaudio_setvolume(dev, dev->ctl_volume);
+ saa7134_tvaudio_init(dev);
saa7134_tvaudio_do_scan(dev);
saa7134_enable_i2s(dev);
saa7134_hw_enable2(dev);
+ saa7134_irq_video_signalchange(dev);
+
/*resume unfinished buffer(s)*/
spin_lock_irqsave(&dev->slock, flags);
saa7134_buffer_requeue(dev, &dev->video_q);
@@ -1246,6 +1258,7 @@ static int saa7134_resume(struct pci_dev *pci_dev)
/* start DMA now*/
dev->insuspend = 0;
+ smp_wmb();
saa7134_set_dmabits(dev);
spin_unlock_irqrestore(&dev->slock, flags);
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index e1ab099ec4c6..a9ca5730826f 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -1073,14 +1073,21 @@ static int dvb_init(struct saa7134_dev *dev)
static int dvb_fini(struct saa7134_dev *dev)
{
- static int on = TDA9887_PRESENT | TDA9887_PORT2_INACTIVE;
+ /* FIXME: I suspect that this code is bogus, since the entry for
+ Pinnacle 300I DVB-T PAL already defines the proper init to allow
+ the detection of mt2032 (TDA9887_PORT2_INACTIVE)
+ */
+ if (dev->board == SAA7134_BOARD_PINNACLE_300I_DVBT_PAL) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+ static int on = TDA9887_PRESENT | TDA9887_PORT2_INACTIVE;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &on;
- switch (dev->board) {
- case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
/* otherwise we don't detect the tuner on next insmod */
- saa7134_i2c_call_clients(dev,TDA9887_SET_CONFIG,&on);
- break;
- };
+ saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &tda9887_cfg);
+ }
+
videobuf_dvb_unregister(&dev->dvb);
return 0;
}
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
index 9322f44865b8..b1b01fa86720 100644
--- a/drivers/media/video/saa7134/saa7134-empress.c
+++ b/drivers/media/video/saa7134/saa7134-empress.c
@@ -161,152 +161,176 @@ ts_mmap(struct file *file, struct vm_area_struct * vma)
* video_generic_ioctl (and maybe others). userspace
* copying is done already, arg is a kernel pointer.
*/
-static int ts_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+
+static int empress_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
{
- struct saa7134_dev *dev = file->private_data;
- struct v4l2_ext_controls *ctrls = arg;
-
- if (debug > 1)
- v4l_print_ioctl(dev->name,cmd);
- switch (cmd) {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
-
- memset(cap,0,sizeof(*cap));
- strcpy(cap->driver, "saa7134");
- strlcpy(cap->card, saa7134_boards[dev->board].name,
- sizeof(cap->card));
- sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
- cap->version = SAA7134_VERSION_CODE;
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- return 0;
- }
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->version = SAA7134_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ return 0;
+}
- /* --- input switching --------------------------------------- */
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *i = arg;
+static int empress_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
- if (i->index != 0)
- return -EINVAL;
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(i->name,"CCIR656");
- return 0;
- }
- case VIDIOC_G_INPUT:
- {
- int *i = arg;
- *i = 0;
- return 0;
- }
- case VIDIOC_S_INPUT:
- {
- int *i = arg;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, "CCIR656");
- if (*i != 0)
- return -EINVAL;
- return 0;
- }
- /* --- capture ioctls ---------------------------------------- */
-
- case VIDIOC_ENUM_FMT:
- {
- struct v4l2_fmtdesc *f = arg;
- int index;
-
- index = f->index;
- if (index != 0)
- return -EINVAL;
-
- memset(f,0,sizeof(*f));
- f->index = index;
- strlcpy(f->description, "MPEG TS", sizeof(f->description));
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- f->pixelformat = V4L2_PIX_FMT_MPEG;
- return 0;
- }
+ return 0;
+}
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *f = arg;
+static int empress_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
- memset(f,0,sizeof(*f));
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+static int empress_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
- saa7134_i2c_call_clients(dev, cmd, arg);
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
- f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets;
- return 0;
- }
+ return 0;
+}
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *f = arg;
+static int empress_enum_fmt_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
+ strlcpy(f->description, "MPEG TS", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
- saa7134_i2c_call_clients(dev, cmd, arg);
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
- f->fmt.pix.sizeimage = TS_PACKET_SIZE* dev->ts.nr_packets;
- return 0;
- }
+ return 0;
+}
- case VIDIOC_REQBUFS:
- return videobuf_reqbufs(&dev->empress_tsq,arg);
+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;
- case VIDIOC_QUERYBUF:
- return videobuf_querybuf(&dev->empress_tsq,arg);
+ saa7134_i2c_call_clients(dev, VIDIOC_G_FMT, f);
- case VIDIOC_QBUF:
- return videobuf_qbuf(&dev->empress_tsq,arg);
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets;
- case VIDIOC_DQBUF:
- return videobuf_dqbuf(&dev->empress_tsq,arg,
- file->f_flags & O_NONBLOCK);
+ return 0;
+}
- case VIDIOC_STREAMON:
- return videobuf_streamon(&dev->empress_tsq);
+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;
- case VIDIOC_STREAMOFF:
- return videobuf_streamoff(&dev->empress_tsq);
+ saa7134_i2c_call_clients(dev, VIDIOC_S_FMT, f);
- case VIDIOC_QUERYCTRL:
- case VIDIOC_G_CTRL:
- case VIDIOC_S_CTRL:
- return saa7134_common_ioctl(dev, cmd, arg);
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets;
- case VIDIOC_S_EXT_CTRLS:
- /* count == 0 is abused in saa6752hs.c, so that special
- case is handled here explicitly. */
- if (ctrls->count == 0)
- return 0;
- if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
- saa7134_i2c_call_clients(dev, VIDIOC_S_EXT_CTRLS, arg);
- ts_init_encoder(dev);
- return 0;
- case VIDIOC_G_EXT_CTRLS:
- if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
- saa7134_i2c_call_clients(dev, VIDIOC_G_EXT_CTRLS, arg);
+ return 0;
+}
+
+
+static int empress_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ return videobuf_reqbufs(&dev->empress_tsq, p);
+}
+
+static int empress_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ 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;
+
+ 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;
+
+ return videobuf_dqbuf(&dev->empress_tsq, b,
+ file->f_flags & O_NONBLOCK);
+}
+
+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;
+
+ return videobuf_streamon(&dev->empress_tsq);
+}
+
+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;
+
+ return videobuf_streamoff(&dev->empress_tsq);
+}
+
+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;
+
+ /* count == 0 is abused in saa6752hs.c, so that special
+ case is handled here explicitly. */
+ if (ctrls->count == 0)
return 0;
- default:
- return -ENOIOCTLCMD;
- }
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ saa7134_i2c_call_clients(dev, VIDIOC_S_EXT_CTRLS, ctrls);
+ ts_init_encoder(dev);
+
return 0;
}
-static int ts_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int empress_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
{
- return video_usercopy(inode, file, cmd, arg, ts_do_ioctl);
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+ saa7134_i2c_call_clients(dev, VIDIOC_G_EXT_CTRLS, ctrls);
+
+ return 0;
}
static const struct file_operations ts_fops =
@@ -317,7 +341,7 @@ static const struct file_operations ts_fops =
.read = ts_read,
.poll = ts_poll,
.mmap = ts_mmap,
- .ioctl = ts_ioctl,
+ .ioctl = video_ioctl2,
.llseek = no_llseek,
};
@@ -330,6 +354,29 @@ static struct video_device saa7134_empress_template =
.type2 = 0 /* FIXME */,
.fops = &ts_fops,
.minor = -1,
+
+ .vidioc_querycap = empress_querycap,
+ .vidioc_enum_fmt_cap = empress_enum_fmt_cap,
+ .vidioc_s_fmt_cap = empress_s_fmt_cap,
+ .vidioc_g_fmt_cap = empress_g_fmt_cap,
+ .vidioc_reqbufs = empress_reqbufs,
+ .vidioc_querybuf = empress_querybuf,
+ .vidioc_qbuf = empress_qbuf,
+ .vidioc_dqbuf = empress_dqbuf,
+ .vidioc_streamon = empress_streamon,
+ .vidioc_streamoff = empress_streamoff,
+ .vidioc_s_ext_ctrls = empress_s_ext_ctrls,
+ .vidioc_g_ext_ctrls = empress_g_ext_ctrls,
+ .vidioc_enum_input = empress_enum_input,
+ .vidioc_g_input = empress_g_input,
+ .vidioc_s_input = empress_s_input,
+
+ .vidioc_queryctrl = saa7134_queryctrl,
+ .vidioc_g_ctrl = saa7134_g_ctrl,
+ .vidioc_s_ctrl = saa7134_s_ctrl,
+
+ .tvnorms = SAA7134_NORMS,
+ .current_norm = V4L2_STD_PAL,
};
static void empress_signal_update(struct work_struct *work)
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
index 6deaad1a5480..d3322c3018f2 100644
--- a/drivers/media/video/saa7134/saa7134-i2c.c
+++ b/drivers/media/video/saa7134/saa7134-i2c.c
@@ -323,7 +323,6 @@ static int attach_inform(struct i2c_client *client)
{
struct saa7134_dev *dev = client->adapter->algo_data;
int tuner = dev->tuner_type;
- int conf = dev->tda9887_conf;
struct tuner_setup tun_setup;
d1printk( "%s i2c attach [addr=0x%x,client=%s]\n",
@@ -335,6 +334,7 @@ static int attach_inform(struct i2c_client *client)
case 0x7a:
case 0x47:
case 0x71:
+ case 0x2d:
{
struct IR_i2c *ir = i2c_get_clientdata(client);
d1printk("%s i2c IR detected (%s).\n",
@@ -360,7 +360,6 @@ static int attach_inform(struct i2c_client *client)
}
if (tuner != UNSET) {
-
tun_setup.type = tuner;
tun_setup.addr = saa7134_boards[dev->board].tuner_addr;
tun_setup.config = saa7134_boards[dev->board].tuner_config;
@@ -372,9 +371,18 @@ static int attach_inform(struct i2c_client *client)
client->driver->command(client,TUNER_SET_TYPE_ADDR, &tun_setup);
}
+
+ if (tuner == TUNER_TDA9887) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &dev->tda9887_conf;
+
+ client->driver->command(client, TUNER_SET_CONFIG,
+ &tda9887_cfg);
+ }
}
- client->driver->command(client, TDA9887_SET_CONFIG, &conf);
return 0;
}
@@ -432,6 +440,7 @@ static char *i2c_devs[128] = {
[ 0xa0 >> 1 ] = "eeprom",
[ 0xc0 >> 1 ] = "tuner (analog)",
[ 0x86 >> 1 ] = "tda9887",
+ [ 0x5a >> 1 ] = "remote control",
};
static void do_i2c_scan(char *name, struct i2c_client *c)
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index 3abaa1b8ac9d..0db955c2d9b9 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -49,9 +49,14 @@ module_param(repeat_delay, int, 0644);
MODULE_PARM_DESC(repeat_delay, "delay before key repeat started");
static int repeat_period = 33;
module_param(repeat_period, int, 0644);
-MODULE_PARM_DESC(repeat_period, "repeat period between"
+MODULE_PARM_DESC(repeat_period, "repeat period between "
"keypresses when key is down");
+static unsigned int disable_other_ir;
+module_param(disable_other_ir, int, 0644);
+MODULE_PARM_DESC(disable_other_ir, "disable full codes of "
+ "alternative remotes from other manufacturers");
+
#define dprintk(fmt, arg...) if (ir_debug) \
printk(KERN_DEBUG "%s/ir: " fmt, dev->name , ## arg)
#define i2cdprintk(fmt, arg...) if (ir_debug) \
@@ -154,6 +159,45 @@ static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
return 1;
}
+
+static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char data[12];
+ u32 gpio;
+
+ struct saa7134_dev *dev = ir->c.adapter->algo_data;
+
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+ if (0x400000 & ~gpio)
+ return 0; /* No button press */
+
+ ir->c.addr = 0x5a >> 1;
+
+ if (12 != i2c_master_recv(&ir->c, data, 12)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+ /* IR of this card normally decode signals NEC-standard from
+ * - Sven IHOO MT 5.1R remote. xxyye718
+ * - Sven DVD HD-10xx remote. xxyyf708
+ * - BBK ...
+ * - mayby others
+ * So, skip not our, if disable full codes mode.
+ */
+ if (data[10] != 0x6b && data[11] != 0x86 && disable_other_ir)
+ return 0;
+
+ *ir_key = data[9];
+ *ir_raw = data[9];
+
+ return 1;
+}
+
void saa7134_input_irq(struct saa7134_dev *dev)
{
struct card_ir *ir = dev->remote;
@@ -260,6 +304,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
+ case SAA7134_BOARD_AVERMEDIA_M102:
ir_codes = ir_codes_avermedia;
mask_keycode = 0x0007C8;
mask_keydown = 0x000010;
@@ -287,6 +332,16 @@ int saa7134_input_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_MANLI_MTV001:
case SAA7134_BOARD_MANLI_MTV002:
case SAA7134_BOARD_BEHOLD_409FM:
+ case SAA7134_BOARD_BEHOLD_401:
+ case SAA7134_BOARD_BEHOLD_403:
+ case SAA7134_BOARD_BEHOLD_403FM:
+ case SAA7134_BOARD_BEHOLD_405:
+ case SAA7134_BOARD_BEHOLD_405FM:
+ case SAA7134_BOARD_BEHOLD_407:
+ case SAA7134_BOARD_BEHOLD_407FM:
+ case SAA7134_BOARD_BEHOLD_409:
+ case SAA7134_BOARD_BEHOLD_505FM:
+ case SAA7134_BOARD_BEHOLD_507_9FM:
ir_codes = ir_codes_manli;
mask_keycode = 0x001f00;
mask_keyup = 0x004000;
@@ -457,6 +512,12 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir)
ir->get_key = get_key_hvr1110;
ir->ir_codes = ir_codes_hauppauge_new;
break;
+ case SAA7134_BOARD_BEHOLD_607_9FM:
+ case SAA7134_BOARD_BEHOLD_M6:
+ snprintf(ir->c.name, sizeof(ir->c.name), "BeholdTV");
+ ir->get_key = get_key_beholdm6xx;
+ ir->ir_codes = ir_codes_behold;
+ break;
default:
dprintk("Shouldn't get here: Unknown board %x for I2C IR?\n",dev->board);
break;
diff --git a/drivers/media/video/saa7134/saa7134-oss.c b/drivers/media/video/saa7134/saa7134-oss.c
deleted file mode 100644
index aedf04653e0e..000000000000
--- a/drivers/media/video/saa7134/saa7134-oss.c
+++ /dev/null
@@ -1,1046 +0,0 @@
-/*
- *
- * device driver for philips saa7134 based TV cards
- * oss dsp interface
- *
- * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
- * 2005 conversion to standalone module:
- * Ricardo Cerqueira <v4l@cerqueira.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/sound.h>
-#include <linux/soundcard.h>
-
-#include "saa7134-reg.h"
-#include "saa7134.h"
-
-/* ------------------------------------------------------------------ */
-
-static unsigned int debug = 0;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug,"enable debug messages [oss]");
-
-static unsigned int rate = 0;
-module_param(rate, int, 0444);
-MODULE_PARM_DESC(rate,"sample rate (valid are: 32000,48000)");
-
-static unsigned int dsp_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
-MODULE_PARM_DESC(dsp_nr, "device numbers for SAA7134 capture interface(s).");
-module_param_array(dsp_nr, int, NULL, 0444);
-
-static unsigned int mixer_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
-MODULE_PARM_DESC(mixer_nr, "mixer numbers for SAA7134 capture interface(s).");
-module_param_array(mixer_nr, int, NULL, 0444);
-
-#define dprintk(fmt, arg...) if (debug) \
- printk(KERN_DEBUG "%s/oss: " fmt, dev->name , ## arg)
-
-
-/* ------------------------------------------------------------------ */
-
-static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks)
-{
- if (blksize < 0x100)
- blksize = 0x100;
- if (blksize > 0x10000)
- blksize = 0x10000;
-
- if (blocks < 2)
- blocks = 2;
- if ((blksize * blocks) > 1024*1024)
- blocks = 1024*1024 / blksize;
-
- dev->dmasound.blocks = blocks;
- dev->dmasound.blksize = blksize;
- dev->dmasound.bufsize = blksize * blocks;
-
- dprintk("buffer config: %d blocks / %d bytes, %d kB total\n",
- blocks,blksize,blksize * blocks / 1024);
- return 0;
-}
-
-static int dsp_buffer_init(struct saa7134_dev *dev)
-{
- int err;
-
- BUG_ON(!dev->dmasound.bufsize);
- videobuf_dma_init(&dev->dmasound.dma);
- err = videobuf_dma_init_kernel(&dev->dmasound.dma, PCI_DMA_FROMDEVICE,
- (dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT);
- if (0 != err)
- return err;
- return 0;
-}
-
-static int dsp_buffer_free(struct saa7134_dev *dev)
-{
- BUG_ON(!dev->dmasound.blksize);
- videobuf_dma_free(&dev->dmasound.dma);
- dev->dmasound.blocks = 0;
- dev->dmasound.blksize = 0;
- dev->dmasound.bufsize = 0;
- return 0;
-}
-
-static void dsp_dma_start(struct saa7134_dev *dev)
-{
- dev->dmasound.dma_blk = 0;
- dev->dmasound.dma_running = 1;
- saa7134_set_dmabits(dev);
-}
-
-static void dsp_dma_stop(struct saa7134_dev *dev)
-{
- dev->dmasound.dma_blk = -1;
- dev->dmasound.dma_running = 0;
- saa7134_set_dmabits(dev);
-}
-
-static int dsp_rec_start(struct saa7134_dev *dev)
-{
- int err, bswap, sign;
- u32 fmt, control;
- unsigned long flags;
-
- /* prepare buffer */
- if (0 != (err = videobuf_pci_dma_map(dev->pci,&dev->dmasound.dma)))
- return err;
- if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt)))
- goto fail1;
- if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt,
- dev->dmasound.dma.sglist,
- dev->dmasound.dma.sglen,
- 0)))
- goto fail2;
-
- /* sample format */
- switch (dev->dmasound.afmt) {
- case AFMT_U8:
- case AFMT_S8: fmt = 0x00; break;
- case AFMT_U16_LE:
- case AFMT_U16_BE:
- case AFMT_S16_LE:
- case AFMT_S16_BE: fmt = 0x01; break;
- default:
- err = -EINVAL;
- goto fail2;
- }
-
- switch (dev->dmasound.afmt) {
- case AFMT_S8:
- case AFMT_S16_LE:
- case AFMT_S16_BE: sign = 1; break;
- default: sign = 0; break;
- }
-
- switch (dev->dmasound.afmt) {
- case AFMT_U16_BE:
- case AFMT_S16_BE: bswap = 1; break;
- default: bswap = 0; break;
- }
-
- switch (dev->pci->device) {
- case PCI_DEVICE_ID_PHILIPS_SAA7134:
- if (1 == dev->dmasound.channels)
- fmt |= (1 << 3);
- if (2 == dev->dmasound.channels)
- fmt |= (3 << 3);
- if (sign)
- fmt |= 0x04;
- fmt |= (TV == dev->dmasound.input) ? 0xc0 : 0x80;
-
- saa_writeb(SAA7134_NUM_SAMPLES0, ((dev->dmasound.blksize - 1) & 0x0000ff));
- saa_writeb(SAA7134_NUM_SAMPLES1, ((dev->dmasound.blksize - 1) & 0x00ff00) >> 8);
- saa_writeb(SAA7134_NUM_SAMPLES2, ((dev->dmasound.blksize - 1) & 0xff0000) >> 16);
- saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt);
-
- break;
- case PCI_DEVICE_ID_PHILIPS_SAA7133:
- case PCI_DEVICE_ID_PHILIPS_SAA7135:
- if (1 == dev->dmasound.channels)
- fmt |= (1 << 4);
- if (2 == dev->dmasound.channels)
- fmt |= (2 << 4);
- if (!sign)
- fmt |= 0x04;
- saa_writel(SAA7133_NUM_SAMPLES, dev->dmasound.blksize -4);
- saa_writel(SAA7133_AUDIO_CHANNEL, 0x543210 | (fmt << 24));
- break;
- }
- dprintk("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c\n",
- dev->dmasound.afmt, dev->dmasound.channels, fmt,
- bswap ? 'b' : '-');
-
- /* dma: setup channel 6 (= AUDIO) */
- control = SAA7134_RS_CONTROL_BURST_16 |
- SAA7134_RS_CONTROL_ME |
- (dev->dmasound.pt.dma >> 12);
- if (bswap)
- control |= SAA7134_RS_CONTROL_BSWAP;
- saa_writel(SAA7134_RS_BA1(6),0);
- saa_writel(SAA7134_RS_BA2(6),dev->dmasound.blksize);
- saa_writel(SAA7134_RS_PITCH(6),0);
- saa_writel(SAA7134_RS_CONTROL(6),control);
-
- /* start dma */
- dev->dmasound.recording_on = 1;
- spin_lock_irqsave(&dev->slock,flags);
- dsp_dma_start(dev);
- spin_unlock_irqrestore(&dev->slock,flags);
- return 0;
-
- fail2:
- saa7134_pgtable_free(dev->pci,&dev->dmasound.pt);
- fail1:
- videobuf_pci_dma_unmap(dev->pci,&dev->dmasound.dma);
- return err;
-}
-
-static int dsp_rec_stop(struct saa7134_dev *dev)
-{
- unsigned long flags;
-
- dprintk("rec_stop dma_blk=%d\n",dev->dmasound.dma_blk);
-
- /* stop dma */
- dev->dmasound.recording_on = 0;
- spin_lock_irqsave(&dev->slock,flags);
- dsp_dma_stop(dev);
- spin_unlock_irqrestore(&dev->slock,flags);
-
- /* unlock buffer */
- saa7134_pgtable_free(dev->pci,&dev->dmasound.pt);
- videobuf_pci_dma_unmap(dev->pci,&dev->dmasound.dma);
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int dsp_open(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- struct saa7134_dev *dev;
- int err;
-
- list_for_each_entry(dev, &saa7134_devlist, devlist)
- if (dev->dmasound.minor_dsp == minor)
- goto found;
- return -ENODEV;
- found:
-
- mutex_lock(&dev->dmasound.lock);
- err = -EBUSY;
- if (dev->dmasound.users_dsp)
- goto fail1;
- dev->dmasound.users_dsp++;
- file->private_data = dev;
-
- dev->dmasound.afmt = AFMT_U8;
- dev->dmasound.channels = 1;
- dev->dmasound.read_count = 0;
- dev->dmasound.read_offset = 0;
- dsp_buffer_conf(dev,PAGE_SIZE,64);
- err = dsp_buffer_init(dev);
- if (0 != err)
- goto fail2;
-
- mutex_unlock(&dev->dmasound.lock);
- return 0;
-
- fail2:
- dev->dmasound.users_dsp--;
- fail1:
- mutex_unlock(&dev->dmasound.lock);
- return err;
-}
-
-static int dsp_release(struct inode *inode, struct file *file)
-{
- struct saa7134_dev *dev = file->private_data;
-
- mutex_lock(&dev->dmasound.lock);
- if (dev->dmasound.recording_on)
- dsp_rec_stop(dev);
- dsp_buffer_free(dev);
- dev->dmasound.users_dsp--;
- file->private_data = NULL;
- mutex_unlock(&dev->dmasound.lock);
- return 0;
-}
-
-static ssize_t dsp_read(struct file *file, char __user *buffer,
- size_t count, loff_t *ppos)
-{
- struct saa7134_dev *dev = file->private_data;
- DECLARE_WAITQUEUE(wait, current);
- unsigned int bytes;
- unsigned long flags;
- int err,ret = 0;
-
- add_wait_queue(&dev->dmasound.wq, &wait);
- mutex_lock(&dev->dmasound.lock);
- while (count > 0) {
- /* wait for data if needed */
- if (0 == dev->dmasound.read_count) {
- if (!dev->dmasound.recording_on) {
- err = dsp_rec_start(dev);
- if (err < 0) {
- if (0 == ret)
- ret = err;
- break;
- }
- }
- if (dev->dmasound.recording_on &&
- !dev->dmasound.dma_running) {
- /* recover from overruns */
- spin_lock_irqsave(&dev->slock,flags);
- dsp_dma_start(dev);
- spin_unlock_irqrestore(&dev->slock,flags);
- }
- if (file->f_flags & O_NONBLOCK) {
- if (0 == ret)
- ret = -EAGAIN;
- break;
- }
- mutex_unlock(&dev->dmasound.lock);
- set_current_state(TASK_INTERRUPTIBLE);
- if (0 == dev->dmasound.read_count)
- schedule();
- set_current_state(TASK_RUNNING);
- mutex_lock(&dev->dmasound.lock);
- if (signal_pending(current)) {
- if (0 == ret)
- ret = -EINTR;
- break;
- }
- }
-
- /* copy data to userspace */
- bytes = count;
- if (bytes > dev->dmasound.read_count)
- bytes = dev->dmasound.read_count;
- if (bytes > dev->dmasound.bufsize - dev->dmasound.read_offset)
- bytes = dev->dmasound.bufsize - dev->dmasound.read_offset;
- if (copy_to_user(buffer + ret,
- dev->dmasound.dma.vmalloc + dev->dmasound.read_offset,
- bytes)) {
- if (0 == ret)
- ret = -EFAULT;
- break;
- }
-
- ret += bytes;
- count -= bytes;
- dev->dmasound.read_count -= bytes;
- dev->dmasound.read_offset += bytes;
- if (dev->dmasound.read_offset == dev->dmasound.bufsize)
- dev->dmasound.read_offset = 0;
- }
- mutex_unlock(&dev->dmasound.lock);
- remove_wait_queue(&dev->dmasound.wq, &wait);
- return ret;
-}
-
-static ssize_t dsp_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *ppos)
-{
- return -EINVAL;
-}
-
-static const char *osspcm_ioctls[] = {
- "RESET", "SYNC", "SPEED", "STEREO", "GETBLKSIZE", "SETFMT",
- "CHANNELS", "?", "POST", "SUBDIVIDE", "SETFRAGMENT", "GETFMTS",
- "GETOSPACE", "GETISPACE", "NONBLOCK", "GETCAPS", "GET/SETTRIGGER",
- "GETIPTR", "GETOPTR", "MAPINBUF", "MAPOUTBUF", "SETSYNCRO",
- "SETDUPLEX", "GETODELAY"
-};
-#define OSSPCM_IOCTLS ARRAY_SIZE(osspcm_ioctls)
-
-static void saa7134_oss_print_ioctl(char *name, unsigned int cmd)
-{
- char *dir;
-
- switch (_IOC_DIR(cmd)) {
- case _IOC_NONE: dir = "--"; break;
- case _IOC_READ: dir = "r-"; break;
- case _IOC_WRITE: dir = "-w"; break;
- case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
- default: dir = "??"; break;
- }
- switch (_IOC_TYPE(cmd)) {
- case 'P':
- printk(KERN_DEBUG "%s: ioctl 0x%08x (oss dsp, %s, SNDCTL_DSP_%s)\n",
- name, cmd, dir, (_IOC_NR(cmd) < OSSPCM_IOCTLS) ?
- osspcm_ioctls[_IOC_NR(cmd)] : "???");
- break;
- case 'M':
- printk(KERN_DEBUG "%s: ioctl 0x%08x (oss mixer, %s, #%d)\n",
- name, cmd, dir, _IOC_NR(cmd));
- break;
- default:
- printk(KERN_DEBUG "%s: ioctl 0x%08x (???, %s, #%d)\n",
- name, cmd, dir, _IOC_NR(cmd));
- }
-}
-
-static int dsp_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct saa7134_dev *dev = file->private_data;
- void __user *argp = (void __user *) arg;
- int __user *p = argp;
- int val = 0;
-
- if (debug > 1)
- saa7134_oss_print_ioctl(dev->name,cmd);
- switch (cmd) {
- case OSS_GETVERSION:
- return put_user(SOUND_VERSION, p);
- case SNDCTL_DSP_GETCAPS:
- return 0;
-
- case SNDCTL_DSP_SPEED:
- if (get_user(val, p))
- return -EFAULT;
- /* fall through */
- case SOUND_PCM_READ_RATE:
- return put_user(dev->dmasound.rate, p);
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, p))
- return -EFAULT;
- mutex_lock(&dev->dmasound.lock);
- dev->dmasound.channels = val ? 2 : 1;
- if (dev->dmasound.recording_on) {
- dsp_rec_stop(dev);
- dsp_rec_start(dev);
- }
- mutex_unlock(&dev->dmasound.lock);
- return put_user(dev->dmasound.channels-1, p);
-
- case SNDCTL_DSP_CHANNELS:
- if (get_user(val, p))
- return -EFAULT;
- if (val != 1 && val != 2)
- return -EINVAL;
- mutex_lock(&dev->dmasound.lock);
- dev->dmasound.channels = val;
- if (dev->dmasound.recording_on) {
- dsp_rec_stop(dev);
- dsp_rec_start(dev);
- }
- mutex_unlock(&dev->dmasound.lock);
- /* fall through */
- case SOUND_PCM_READ_CHANNELS:
- return put_user(dev->dmasound.channels, p);
-
- case SNDCTL_DSP_GETFMTS: /* Returns a mask */
- return put_user(AFMT_U8 | AFMT_S8 |
- AFMT_U16_LE | AFMT_U16_BE |
- AFMT_S16_LE | AFMT_S16_BE, p);
-
- case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */
- if (get_user(val, p))
- return -EFAULT;
- switch (val) {
- case AFMT_QUERY:
- /* nothing to do */
- break;
- case AFMT_U8:
- case AFMT_S8:
- case AFMT_U16_LE:
- case AFMT_U16_BE:
- case AFMT_S16_LE:
- case AFMT_S16_BE:
- mutex_lock(&dev->dmasound.lock);
- dev->dmasound.afmt = val;
- if (dev->dmasound.recording_on) {
- dsp_rec_stop(dev);
- dsp_rec_start(dev);
- }
- mutex_unlock(&dev->dmasound.lock);
- return put_user(dev->dmasound.afmt, p);
- default:
- return -EINVAL;
- }
-
- case SOUND_PCM_READ_BITS:
- switch (dev->dmasound.afmt) {
- case AFMT_U8:
- case AFMT_S8:
- return put_user(8, p);
- case AFMT_U16_LE:
- case AFMT_U16_BE:
- case AFMT_S16_LE:
- case AFMT_S16_BE:
- return put_user(16, p);
- default:
- return -EINVAL;
- }
-
- case SNDCTL_DSP_NONBLOCK:
- file->f_flags |= O_NONBLOCK;
- return 0;
-
- case SNDCTL_DSP_RESET:
- mutex_lock(&dev->dmasound.lock);
- if (dev->dmasound.recording_on)
- dsp_rec_stop(dev);
- mutex_unlock(&dev->dmasound.lock);
- return 0;
- case SNDCTL_DSP_GETBLKSIZE:
- return put_user(dev->dmasound.blksize, p);
-
- case SNDCTL_DSP_SETFRAGMENT:
- if (get_user(val, p))
- return -EFAULT;
- if (dev->dmasound.recording_on)
- return -EBUSY;
- dsp_buffer_free(dev);
- /* used to be arg >> 16 instead of val >> 16; fixed */
- dsp_buffer_conf(dev,1 << (val & 0xffff), (val >> 16) & 0xffff);
- dsp_buffer_init(dev);
- return 0;
-
- case SNDCTL_DSP_SYNC:
- /* NOP */
- return 0;
-
- case SNDCTL_DSP_GETISPACE:
- {
- audio_buf_info info;
- info.fragsize = dev->dmasound.blksize;
- info.fragstotal = dev->dmasound.blocks;
- info.bytes = dev->dmasound.read_count;
- info.fragments = info.bytes / info.fragsize;
- if (copy_to_user(argp, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- default:
- return -EINVAL;
- }
-}
-
-static unsigned int dsp_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct saa7134_dev *dev = file->private_data;
- unsigned int mask = 0;
-
- poll_wait(file, &dev->dmasound.wq, wait);
-
- if (0 == dev->dmasound.read_count) {
- mutex_lock(&dev->dmasound.lock);
- if (!dev->dmasound.recording_on)
- dsp_rec_start(dev);
- mutex_unlock(&dev->dmasound.lock);
- } else
- mask |= (POLLIN | POLLRDNORM);
- return mask;
-}
-
-const struct file_operations saa7134_dsp_fops = {
- .owner = THIS_MODULE,
- .open = dsp_open,
- .release = dsp_release,
- .read = dsp_read,
- .write = dsp_write,
- .ioctl = dsp_ioctl,
- .poll = dsp_poll,
- .llseek = no_llseek,
-};
-
-/* ------------------------------------------------------------------ */
-
-static int
-mixer_recsrc_7134(struct saa7134_dev *dev)
-{
- int analog_io,rate;
-
- switch (dev->dmasound.input) {
- case TV:
- saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0xc0);
- saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x00);
- break;
- case LINE1:
- case LINE2:
- case LINE2_LEFT:
- analog_io = (LINE1 == dev->dmasound.input) ? 0x00 : 0x08;
- rate = (32000 == dev->dmasound.rate) ? 0x01 : 0x03;
- saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, analog_io);
- saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0x80);
- saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, rate);
- break;
- }
- return 0;
-}
-
-static int
-mixer_recsrc_7133(struct saa7134_dev *dev)
-{
- u32 anabar, xbarin;
-
- xbarin = 0x03; // adc
- anabar = 0;
- switch (dev->dmasound.input) {
- case TV:
- xbarin = 0; // Demodulator
- anabar = 2; // DACs
- break;
- case LINE1:
- anabar = 0; // aux1, aux1
- break;
- case LINE2:
- case LINE2_LEFT:
- anabar = 9; // aux2, aux2
- break;
- }
- /* output xbar always main channel */
- saa_dsp_writel(dev, 0x46c >> 2, 0xbbbb10);
- saa_dsp_writel(dev, 0x464 >> 2, xbarin);
- saa_writel(0x594 >> 2, anabar);
-
- return 0;
-}
-
-static int
-mixer_recsrc(struct saa7134_dev *dev, enum saa7134_audio_in src)
-{
- static const char *iname[] = { "Oops", "TV", "LINE1", "LINE2" };
-
- dev->dmasound.count++;
- dev->dmasound.input = src;
- dprintk("mixer input = %s\n",iname[dev->dmasound.input]);
-
- switch (dev->pci->device) {
- case PCI_DEVICE_ID_PHILIPS_SAA7134:
- mixer_recsrc_7134(dev);
- break;
- case PCI_DEVICE_ID_PHILIPS_SAA7133:
- case PCI_DEVICE_ID_PHILIPS_SAA7135:
- mixer_recsrc_7133(dev);
- break;
- }
- return 0;
-}
-
-static int
-mixer_level(struct saa7134_dev *dev, enum saa7134_audio_in src, int level)
-{
- switch (dev->pci->device) {
- case PCI_DEVICE_ID_PHILIPS_SAA7134:
- switch (src) {
- case TV:
- /* nothing */
- break;
- case LINE1:
- saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10,
- (100 == level) ? 0x00 : 0x10);
- break;
- case LINE2:
- case LINE2_LEFT:
- saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20,
- (100 == level) ? 0x00 : 0x20);
- break;
- }
- break;
- case PCI_DEVICE_ID_PHILIPS_SAA7133:
- case PCI_DEVICE_ID_PHILIPS_SAA7135:
- /* nothing */
- break;
- }
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int mixer_open(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- struct saa7134_dev *dev;
-
- list_for_each_entry(dev, &saa7134_devlist, devlist)
- if (dev->dmasound.minor_mixer == minor) {
- file->private_data = dev;
- return 0;
- }
- return -ENODEV;
-}
-
-static int mixer_release(struct inode *inode, struct file *file)
-{
- file->private_data = NULL;
- return 0;
-}
-
-static int mixer_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct saa7134_dev *dev = file->private_data;
- enum saa7134_audio_in input;
- int val,ret;
- void __user *argp = (void __user *) arg;
- int __user *p = argp;
-
- if (debug > 1)
- saa7134_oss_print_ioctl(dev->name,cmd);
- switch (cmd) {
- case OSS_GETVERSION:
- return put_user(SOUND_VERSION, p);
- case SOUND_MIXER_INFO:
- {
- mixer_info info;
- memset(&info,0,sizeof(info));
- strlcpy(info.id, "TV audio", sizeof(info.id));
- strlcpy(info.name, dev->name, sizeof(info.name));
- info.modify_counter = dev->dmasound.count;
- if (copy_to_user(argp, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- case SOUND_OLD_MIXER_INFO:
- {
- _old_mixer_info info;
- memset(&info,0,sizeof(info));
- strlcpy(info.id, "TV audio", sizeof(info.id));
- strlcpy(info.name, dev->name, sizeof(info.name));
- if (copy_to_user(argp, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- case MIXER_READ(SOUND_MIXER_CAPS):
- return put_user(SOUND_CAP_EXCL_INPUT, p);
- case MIXER_READ(SOUND_MIXER_STEREODEVS):
- return put_user(0, p);
- case MIXER_READ(SOUND_MIXER_RECMASK):
- case MIXER_READ(SOUND_MIXER_DEVMASK):
- val = SOUND_MASK_LINE1 | SOUND_MASK_LINE2;
- if (32000 == dev->dmasound.rate)
- val |= SOUND_MASK_VIDEO;
- return put_user(val, p);
-
- case MIXER_WRITE(SOUND_MIXER_RECSRC):
- if (get_user(val, p))
- return -EFAULT;
- input = dev->dmasound.input;
- if (32000 == dev->dmasound.rate &&
- val & SOUND_MASK_VIDEO && dev->dmasound.input != TV)
- input = TV;
- if (val & SOUND_MASK_LINE1 && dev->dmasound.input != LINE1)
- input = LINE1;
- if (val & SOUND_MASK_LINE2 && dev->dmasound.input != LINE2)
- input = LINE2;
- if (input != dev->dmasound.input)
- mixer_recsrc(dev,input);
- /* fall throuth */
- case MIXER_READ(SOUND_MIXER_RECSRC):
- switch (dev->dmasound.input) {
- case TV: ret = SOUND_MASK_VIDEO; break;
- case LINE1: ret = SOUND_MASK_LINE1; break;
- case LINE2: ret = SOUND_MASK_LINE2; break;
- default: ret = 0;
- }
- return put_user(ret, p);
-
- case MIXER_WRITE(SOUND_MIXER_VIDEO):
- case MIXER_READ(SOUND_MIXER_VIDEO):
- if (32000 != dev->dmasound.rate)
- return -EINVAL;
- return put_user(100 | 100 << 8, p);
-
- case MIXER_WRITE(SOUND_MIXER_LINE1):
- if (get_user(val, p))
- return -EFAULT;
- val &= 0xff;
- val = (val <= 50) ? 50 : 100;
- dev->dmasound.line1 = val;
- mixer_level(dev,LINE1,dev->dmasound.line1);
- /* fall throuth */
- case MIXER_READ(SOUND_MIXER_LINE1):
- return put_user(dev->dmasound.line1 | dev->dmasound.line1 << 8, p);
-
- case MIXER_WRITE(SOUND_MIXER_LINE2):
- if (get_user(val, p))
- return -EFAULT;
- val &= 0xff;
- val = (val <= 50) ? 50 : 100;
- dev->dmasound.line2 = val;
- mixer_level(dev,LINE2,dev->dmasound.line2);
- /* fall throuth */
- case MIXER_READ(SOUND_MIXER_LINE2):
- return put_user(dev->dmasound.line2 | dev->dmasound.line2 << 8, p);
-
- default:
- return -EINVAL;
- }
-}
-
-const struct file_operations saa7134_mixer_fops = {
- .owner = THIS_MODULE,
- .open = mixer_open,
- .release = mixer_release,
- .ioctl = mixer_ioctl,
- .llseek = no_llseek,
-};
-
-/* ------------------------------------------------------------------ */
-
-static irqreturn_t saa7134_oss_irq(int irq, void *dev_id)
-{
- struct saa7134_dmasound *dmasound = dev_id;
- struct saa7134_dev *dev = dmasound->priv_data;
- unsigned long report, status;
- int loop, handled = 0;
-
- for (loop = 0; loop < 10; loop++) {
- report = saa_readl(SAA7134_IRQ_REPORT);
- status = saa_readl(SAA7134_IRQ_STATUS);
-
- if (report & SAA7134_IRQ_REPORT_DONE_RA3) {
- handled = 1;
- saa_writel(SAA7134_IRQ_REPORT,report);
- saa7134_irq_oss_done(dev, status);
- } else {
- goto out;
- }
- }
-
- if (loop == 10) {
- dprintk("error! looping IRQ!");
- }
-out:
- return IRQ_RETVAL(handled);
-}
-
-int saa7134_oss_init1(struct saa7134_dev *dev)
-{
-
- if ((request_irq(dev->pci->irq, saa7134_oss_irq,
- IRQF_SHARED | IRQF_DISABLED, dev->name,
- (void*) &dev->dmasound)) < 0)
- return -1;
-
- /* general */
- mutex_init(&dev->dmasound.lock);
- init_waitqueue_head(&dev->dmasound.wq);
-
- switch (dev->pci->device) {
- case PCI_DEVICE_ID_PHILIPS_SAA7133:
- case PCI_DEVICE_ID_PHILIPS_SAA7135:
- saa_writel(0x588 >> 2, 0x00000fff);
- saa_writel(0x58c >> 2, 0x00543210);
- saa_dsp_writel(dev, 0x46c >> 2, 0xbbbbbb);
- break;
- }
-
- /* dsp */
- dev->dmasound.rate = 32000;
- if (rate)
- dev->dmasound.rate = rate;
- dev->dmasound.rate = (dev->dmasound.rate > 40000) ? 48000 : 32000;
-
- /* mixer */
- dev->dmasound.line1 = 50;
- dev->dmasound.line2 = 50;
- mixer_level(dev,LINE1,dev->dmasound.line1);
- mixer_level(dev,LINE2,dev->dmasound.line2);
- mixer_recsrc(dev, (dev->dmasound.rate == 32000) ? TV : LINE2);
-
- return 0;
-}
-
-int saa7134_oss_fini(struct saa7134_dev *dev)
-{
- /* nothing */
- return 0;
-}
-
-void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status)
-{
- int next_blk, reg = 0;
-
- spin_lock(&dev->slock);
- if (UNSET == dev->dmasound.dma_blk) {
- dprintk("irq: recording stopped\n");
- goto done;
- }
- if (0 != (status & 0x0f000000))
- dprintk("irq: lost %ld\n", (status >> 24) & 0x0f);
- if (0 == (status & 0x10000000)) {
- /* odd */
- if (0 == (dev->dmasound.dma_blk & 0x01))
- reg = SAA7134_RS_BA1(6);
- } else {
- /* even */
- if (1 == (dev->dmasound.dma_blk & 0x01))
- reg = SAA7134_RS_BA2(6);
- }
- if (0 == reg) {
- dprintk("irq: field oops [%s]\n",
- (status & 0x10000000) ? "even" : "odd");
- goto done;
- }
- if (dev->dmasound.read_count >= dev->dmasound.blksize * (dev->dmasound.blocks-2)) {
- dprintk("irq: overrun [full=%d/%d]\n",dev->dmasound.read_count,
- dev->dmasound.bufsize);
- dsp_dma_stop(dev);
- goto done;
- }
-
- /* next block addr */
- next_blk = (dev->dmasound.dma_blk + 2) % dev->dmasound.blocks;
- saa_writel(reg,next_blk * dev->dmasound.blksize);
- if (debug > 2)
- dprintk("irq: ok, %s, next_blk=%d, addr=%x\n",
- (status & 0x10000000) ? "even" : "odd ", next_blk,
- next_blk * dev->dmasound.blksize);
-
- /* update status & wake waiting readers */
- dev->dmasound.dma_blk = (dev->dmasound.dma_blk + 1) % dev->dmasound.blocks;
- dev->dmasound.read_count += dev->dmasound.blksize;
- wake_up(&dev->dmasound.wq);
-
- done:
- spin_unlock(&dev->slock);
-}
-
-static int saa7134_dsp_create(struct saa7134_dev *dev)
-{
- int err;
-
- err = dev->dmasound.minor_dsp =
- register_sound_dsp(&saa7134_dsp_fops,
- dsp_nr[dev->nr]);
- if (err < 0) {
- goto fail;
- }
- printk(KERN_INFO "%s: registered device dsp%d\n",
- dev->name,dev->dmasound.minor_dsp >> 4);
-
- err = dev->dmasound.minor_mixer =
- register_sound_mixer(&saa7134_mixer_fops,
- mixer_nr[dev->nr]);
- if (err < 0)
- goto fail;
- printk(KERN_INFO "%s: registered device mixer%d\n",
- dev->name,dev->dmasound.minor_mixer >> 4);
-
- return 0;
-
-fail:
- unregister_sound_dsp(dev->dmasound.minor_dsp);
- return 0;
-
-
-}
-
-static int oss_device_init(struct saa7134_dev *dev)
-{
- dev->dmasound.priv_data = dev;
- saa7134_oss_init1(dev);
- saa7134_dsp_create(dev);
- return 1;
-}
-
-static int oss_device_exit(struct saa7134_dev *dev)
-{
-
- unregister_sound_mixer(dev->dmasound.minor_mixer);
- unregister_sound_dsp(dev->dmasound.minor_dsp);
-
- saa7134_oss_fini(dev);
-
- if (dev->pci->irq > 0) {
- synchronize_irq(dev->pci->irq);
- free_irq(dev->pci->irq,&dev->dmasound);
- }
-
- dev->dmasound.priv_data = NULL;
- return 1;
-}
-
-static int saa7134_oss_init(void)
-{
- struct saa7134_dev *dev = NULL;
- struct list_head *list;
-
- if (!saa7134_dmasound_init && !saa7134_dmasound_exit) {
- saa7134_dmasound_init = oss_device_init;
- saa7134_dmasound_exit = oss_device_exit;
- } else {
- printk(KERN_WARNING "saa7134 OSS: can't load, DMA sound handler already assigned (probably to ALSA)\n");
- return -EBUSY;
- }
-
- printk(KERN_INFO "saa7134 OSS driver for DMA sound loaded\n");
-
-
- list_for_each(list,&saa7134_devlist) {
- dev = list_entry(list, struct saa7134_dev, devlist);
- if (dev->dmasound.priv_data == NULL) {
- oss_device_init(dev);
- } else {
- printk(KERN_ERR "saa7134 OSS: DMA sound is being handled by ALSA, ignoring %s\n",dev->name);
- return -EBUSY;
- }
- }
-
- if (dev == NULL)
- printk(KERN_INFO "saa7134 OSS: no saa7134 cards found\n");
-
- return 0;
-
-}
-
-static void saa7134_oss_exit(void)
-{
- struct saa7134_dev *dev;
-
- list_for_each_entry(dev, &saa7134_devlist, devlist) {
- /* Device isn't registered by OSS, probably ALSA's */
- if (!dev->dmasound.minor_dsp)
- continue;
-
- oss_device_exit(dev);
- }
-
- saa7134_dmasound_init = NULL;
- saa7134_dmasound_exit = NULL;
-
- printk(KERN_INFO "saa7134 OSS driver for DMA sound unloaded\n");
-
- return;
-}
-
-/* We initialize this late, to make sure the sound system is up and running */
-late_initcall(saa7134_oss_init);
-module_exit(saa7134_oss_exit);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/saa7134/saa7134-ts.c b/drivers/media/video/saa7134/saa7134-ts.c
index 4b63ad3e8466..f1b8fcaeb43a 100644
--- a/drivers/media/video/saa7134/saa7134-ts.c
+++ b/drivers/media/video/saa7134/saa7134-ts.c
@@ -47,7 +47,7 @@ static int buffer_activate(struct saa7134_dev *dev,
{
dprintk("buffer_activate [%p]",buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->top_seen = 0;
if (NULL == next)
@@ -91,7 +91,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
saa7134_dma_free(q,buf);
}
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
buf->vb.width = llength;
@@ -121,7 +121,7 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
saa_writel(SAA7134_RS_PITCH(5),TS_PACKET_SIZE);
saa_writel(SAA7134_RS_CONTROL(5),control);
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
buf->activate = buffer_activate;
buf->vb.field = field;
return 0;
@@ -242,7 +242,7 @@ void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status)
if ((status & 0x100000) != 0x100000)
goto done;
}
- saa7134_buffer_finish(dev,&dev->ts_q,STATE_DONE);
+ saa7134_buffer_finish(dev,&dev->ts_q,VIDEOBUF_DONE);
}
saa7134_buffer_next(dev,&dev->ts_q);
diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c
index f8e304c76232..4e9810469ae3 100644
--- a/drivers/media/video/saa7134/saa7134-tvaudio.c
+++ b/drivers/media/video/saa7134/saa7134-tvaudio.c
@@ -163,32 +163,6 @@ static struct saa7134_tvaudio tvaudio[] = {
/* ------------------------------------------------------------------ */
-static void tvaudio_init(struct saa7134_dev *dev)
-{
- int clock = saa7134_boards[dev->board].audio_clock;
-
- if (UNSET != audio_clock_override)
- clock = audio_clock_override;
-
- /* init all audio registers */
- saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x00);
- if (need_resched())
- schedule();
- else
- udelay(10);
-
- saa_writeb(SAA7134_AUDIO_CLOCK0, clock & 0xff);
- saa_writeb(SAA7134_AUDIO_CLOCK1, (clock >> 8) & 0xff);
- saa_writeb(SAA7134_AUDIO_CLOCK2, (clock >> 16) & 0xff);
- /* frame locked audio is mandatory for NICAM */
- saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x01);
-
- saa_writeb(SAA7134_NICAM_ERROR_LOW, 0x14);
- saa_writeb(SAA7134_NICAM_ERROR_HIGH, 0x50);
- saa_writeb(SAA7134_MONITOR_SELECT, 0xa0);
- saa_writeb(SAA7134_FM_DEMATRIX, 0x80);
-}
-
static u32 tvaudio_carr2reg(u32 carrier)
{
u64 a = carrier;
@@ -517,9 +491,13 @@ static int tvaudio_thread(void *data)
dev->thread.scan1 = dev->thread.scan2;
dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1);
dev->tvaudio = NULL;
- tvaudio_init(dev);
+
+ saa_writeb(SAA7134_MONITOR_SELECT, 0xa0);
+ saa_writeb(SAA7134_FM_DEMATRIX, 0x80);
+
if (dev->ctl_automute)
dev->automute = 1;
+
mute_input_7134(dev);
/* give the tuner some time */
@@ -784,27 +762,15 @@ static int mute_input_7133(struct saa7134_dev *dev)
static int tvaudio_thread_ddep(void *data)
{
struct saa7134_dev *dev = data;
- u32 value, norms, clock;
+ u32 value, norms;
set_freezable();
-
- clock = saa7134_boards[dev->board].audio_clock;
- if (UNSET != audio_clock_override)
- clock = audio_clock_override;
- saa_writel(0x598 >> 2, clock);
-
- /* unmute */
- saa_dsp_writel(dev, 0x474 >> 2, 0x00);
- saa_dsp_writel(dev, 0x450 >> 2, 0x00);
-
for (;;) {
tvaudio_sleep(dev,-1);
if (kthread_should_stop())
goto done;
-
restart:
-
try_to_freeze();
dev->thread.scan1 = dev->thread.scan2;
@@ -978,6 +944,38 @@ int saa7134_tvaudio_getstereo(struct saa7134_dev *dev)
return retval;
}
+void saa7134_tvaudio_init(struct saa7134_dev *dev)
+{
+ int clock = saa7134_boards[dev->board].audio_clock;
+
+ if (UNSET != audio_clock_override)
+ clock = audio_clock_override;
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ /* init all audio registers */
+ saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x00);
+ if (need_resched())
+ schedule();
+ else
+ udelay(10);
+
+ saa_writeb(SAA7134_AUDIO_CLOCK0, clock & 0xff);
+ saa_writeb(SAA7134_AUDIO_CLOCK1, (clock >> 8) & 0xff);
+ saa_writeb(SAA7134_AUDIO_CLOCK2, (clock >> 16) & 0xff);
+ /* frame locked audio is mandatory for NICAM */
+ saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x01);
+ saa_writeb(SAA7134_NICAM_ERROR_LOW, 0x14);
+ saa_writeb(SAA7134_NICAM_ERROR_HIGH, 0x50);
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ saa_writel(0x598 >> 2, clock);
+ saa_dsp_writel(dev, 0x474 >> 2, 0x00);
+ saa_dsp_writel(dev, 0x450 >> 2, 0x00);
+ }
+}
+
int saa7134_tvaudio_init2(struct saa7134_dev *dev)
{
int (*my_thread)(void *data) = NULL;
@@ -994,6 +992,7 @@ int saa7134_tvaudio_init2(struct saa7134_dev *dev)
dev->thread.thread = NULL;
if (my_thread) {
+ saa7134_tvaudio_init(dev);
/* start tvaudio thread */
dev->thread.thread = kthread_run(my_thread, dev, "%s", dev->name);
if (IS_ERR(dev->thread.thread)) {
diff --git a/drivers/media/video/saa7134/saa7134-vbi.c b/drivers/media/video/saa7134/saa7134-vbi.c
index 81a2aedeff5c..f0d5ed9c2b06 100644
--- a/drivers/media/video/saa7134/saa7134-vbi.c
+++ b/drivers/media/video/saa7134/saa7134-vbi.c
@@ -85,7 +85,7 @@ static int buffer_activate(struct saa7134_dev *dev,
unsigned long control,base;
dprintk("buffer_activate [%p]\n",buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->top_seen = 0;
task_init(dev,buf,TASK_A);
@@ -136,7 +136,7 @@ static int buffer_prepare(struct videobuf_queue *q,
if (buf->vb.size != size)
saa7134_dma_free(q,buf);
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
buf->vb.width = llength;
@@ -154,7 +154,7 @@ static int buffer_prepare(struct videobuf_queue *q,
if (err)
goto oops;
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
buf->activate = buffer_activate;
buf->vb.field = field;
return 0;
@@ -240,7 +240,7 @@ void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status)
goto done;
dev->vbi_q.curr->vb.field_count = dev->vbi_fieldcount;
- saa7134_buffer_finish(dev,&dev->vbi_q,STATE_DONE);
+ saa7134_buffer_finish(dev,&dev->vbi_q,VIDEOBUF_DONE);
}
saa7134_buffer_next(dev,&dev->vbi_q);
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index 6396d9b5c063..1184d359e848 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -38,7 +38,7 @@
/* ------------------------------------------------------------------ */
-static unsigned int video_debug = 0;
+unsigned int video_debug;
static unsigned int gbuffers = 8;
static unsigned int noninterlaced = 0;
static unsigned int gbufsize = 720*576*4;
@@ -54,7 +54,7 @@ module_param_string(secam, secam, sizeof(secam), 0644);
MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc");
-#define dprintk(fmt, arg...) if (video_debug) \
+#define dprintk(fmt, arg...) if (video_debug&0x04) \
printk(KERN_DEBUG "%s/video: " fmt, dev->name , ## arg)
/* ------------------------------------------------------------------ */
@@ -540,9 +540,8 @@ void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits)
/* ------------------------------------------------------------------ */
-void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
+static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
{
-
dprintk("set tv norm = %s\n",norm->name);
dev->tvnorm = norm;
@@ -561,7 +560,6 @@ void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
dev->crop_current = dev->crop_defrect;
saa7134_set_tvnorm_hw(dev);
-
}
static void video_mux(struct saa7134_dev *dev, int input)
@@ -945,7 +943,7 @@ static int buffer_activate(struct saa7134_dev *dev,
unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */
dprintk("buffer_activate buf=%p\n",buf);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
buf->top_seen = 0;
set_size(dev,TASK_A,buf->vb.width,buf->vb.height,
@@ -1054,7 +1052,7 @@ static int buffer_prepare(struct videobuf_queue *q,
saa7134_dma_free(q,buf);
}
- if (STATE_NEEDS_INIT == buf->vb.state) {
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
buf->vb.width = fh->width;
@@ -1074,7 +1072,7 @@ static int buffer_prepare(struct videobuf_queue *q,
if (err)
goto oops;
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
buf->activate = buffer_activate;
return 0;
@@ -1119,8 +1117,10 @@ static struct videobuf_queue_ops video_qops = {
/* ------------------------------------------------------------------ */
-static int get_control(struct saa7134_dev *dev, struct v4l2_control *c)
+int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c)
{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
const struct v4l2_queryctrl* ctrl;
ctrl = ctrl_by_id(c->id);
@@ -1165,17 +1165,27 @@ static int get_control(struct saa7134_dev *dev, struct v4l2_control *c)
}
return 0;
}
+EXPORT_SYMBOL_GPL(saa7134_g_ctrl);
-static int set_control(struct saa7134_dev *dev, struct saa7134_fh *fh,
- struct v4l2_control *c)
+int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c)
{
const struct v4l2_queryctrl* ctrl;
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
unsigned long flags;
int restart_overlay = 0;
+ int err = -EINVAL;
+
+ err = v4l2_prio_check(&dev->prio, &fh->prio);
+ if (0 != err)
+ return err;
+
+ mutex_lock(&dev->lock);
ctrl = ctrl_by_id(c->id);
if (NULL == ctrl)
- return -EINVAL;
+ goto error;
+
dprintk("set_control name=%s val=%d\n",ctrl->name,c->value);
switch (ctrl->type) {
case V4L2_CTRL_TYPE_BOOLEAN:
@@ -1236,18 +1246,26 @@ static int set_control(struct saa7134_dev *dev, struct saa7134_fh *fh,
restart_overlay = 1;
break;
case V4L2_CID_PRIVATE_AUTOMUTE:
+ {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &dev->tda9887_conf;
+
dev->ctl_automute = c->value;
if (dev->tda9887_conf) {
if (dev->ctl_automute)
dev->tda9887_conf |= TDA9887_AUTOMUTE;
else
dev->tda9887_conf &= ~TDA9887_AUTOMUTE;
- saa7134_i2c_call_clients(dev, TDA9887_SET_CONFIG,
- &dev->tda9887_conf);
+
+ saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG,
+ &tda9887_cfg);
}
break;
+ }
default:
- return -EINVAL;
+ goto error;
}
if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) {
spin_lock_irqsave(&dev->slock,flags);
@@ -1255,8 +1273,13 @@ static int set_control(struct saa7134_dev *dev, struct saa7134_fh *fh,
start_preview(dev,fh);
spin_unlock_irqrestore(&dev->slock,flags);
}
- return 0;
+ err = 0;
+
+error:
+ mutex_unlock(&dev->lock);
+ return err;
}
+EXPORT_SYMBOL_GPL(saa7134_s_ctrl);
/* ------------------------------------------------------------------ */
@@ -1413,8 +1436,8 @@ video_poll(struct file *file, struct poll_table_struct *wait)
return POLLERR;
poll_wait(file, &buf->done, wait);
- if (buf->state == STATE_DONE ||
- buf->state == STATE_ERROR)
+ if (buf->state == VIDEOBUF_DONE ||
+ buf->state == VIDEOBUF_ERROR)
return POLLIN|POLLRDNORM;
return 0;
}
@@ -1478,8 +1501,11 @@ static int video_mmap(struct file *file, struct vm_area_struct * vma)
/* ------------------------------------------------------------------ */
-static void saa7134_vbi_fmt(struct saa7134_dev *dev, struct v4l2_format *f)
+static int saa7134_try_get_set_fmt_vbi(struct file *file, void *priv,
+ struct v4l2_format *f)
{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
struct saa7134_tvnorm *norm = dev->tvnorm;
f->fmt.vbi.sampling_rate = 6750000 * 4;
@@ -1492,837 +1518,805 @@ static void saa7134_vbi_fmt(struct saa7134_dev *dev, struct v4l2_format *f)
f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */
+ return 0;
}
-static int saa7134_g_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh,
- struct v4l2_format *f)
+static int saa7134_g_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- memset(&f->fmt.pix,0,sizeof(f->fmt.pix));
- f->fmt.pix.width = fh->width;
- f->fmt.pix.height = fh->height;
- f->fmt.pix.field = fh->cap.field;
- f->fmt.pix.pixelformat = fh->fmt->fourcc;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fh->fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
- return 0;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (saa7134_no_overlay > 0) {
- printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
- f->fmt.win = fh->win;
- return 0;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- saa7134_vbi_fmt(dev,f);
- return 0;
- default:
- return -EINVAL;
- }
+ struct saa7134_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->cap.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ return 0;
}
-static int saa7134_try_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh,
- struct v4l2_format *f)
+static int saa7134_g_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- int err;
+ struct saa7134_fh *fh = priv;
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- {
- struct saa7134_format *fmt;
- enum v4l2_field field;
- unsigned int maxw, maxh;
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+ f->fmt.win = fh->win;
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
+ return 0;
+}
- field = f->fmt.pix.field;
- maxw = min(dev->crop_current.width*4, dev->crop_bounds.width);
- maxh = min(dev->crop_current.height*4, dev->crop_bounds.height);
+static int saa7134_try_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_format *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
- if (V4L2_FIELD_ANY == field) {
- field = (f->fmt.pix.height > maxh/2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- }
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- return -EINVAL;
- }
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
- f->fmt.pix.field = field;
- if (f->fmt.pix.width < 48)
- f->fmt.pix.width = 48;
- if (f->fmt.pix.height < 32)
- f->fmt.pix.height = 32;
- if (f->fmt.pix.width > maxw)
- f->fmt.pix.width = maxw;
- if (f->fmt.pix.height > maxh)
- f->fmt.pix.height = maxh;
- f->fmt.pix.width &= ~0x03;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
+ field = f->fmt.pix.field;
+ maxw = min(dev->crop_current.width*4, dev->crop_bounds.width);
+ maxh = min(dev->crop_current.height*4, dev->crop_bounds.height);
- return 0;
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
}
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (saa7134_no_overlay > 0) {
- printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
- err = verify_preview(dev,&f->fmt.win);
- if (0 != err)
- return err;
- return 0;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- saa7134_vbi_fmt(dev,f);
- return 0;
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
default:
return -EINVAL;
}
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ f->fmt.pix.width &= ~0x03;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
}
-static int saa7134_s_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh,
- struct v4l2_format *f)
+static int saa7134_try_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- unsigned long flags;
- int err;
-
- switch (f->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- err = saa7134_try_fmt(dev,fh,f);
- if (0 != err)
- return err;
-
- fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- fh->width = f->fmt.pix.width;
- fh->height = f->fmt.pix.height;
- fh->cap.field = f->fmt.pix.field;
- return 0;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (saa7134_no_overlay > 0) {
- printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
- err = verify_preview(dev,&f->fmt.win);
- if (0 != err)
- return err;
-
- mutex_lock(&dev->lock);
- fh->win = f->fmt.win;
- fh->nclips = f->fmt.win.clipcount;
- if (fh->nclips > 8)
- fh->nclips = 8;
- if (copy_from_user(fh->clips,f->fmt.win.clips,
- sizeof(struct v4l2_clip)*fh->nclips)) {
- mutex_unlock(&dev->lock);
- return -EFAULT;
- }
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
- if (res_check(fh, RESOURCE_OVERLAY)) {
- spin_lock_irqsave(&dev->slock,flags);
- stop_preview(dev,fh);
- start_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- }
- mutex_unlock(&dev->lock);
- return 0;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- saa7134_vbi_fmt(dev,f);
- return 0;
- default:
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
+
+ return verify_preview(dev, &f->fmt.win);
}
-int saa7134_common_ioctl(struct saa7134_dev *dev,
- unsigned int cmd, void *arg)
+static int saa7134_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
+ struct saa7134_fh *fh = priv;
int err;
- switch (cmd) {
- case VIDIOC_QUERYCTRL:
- {
- const struct v4l2_queryctrl *ctrl;
- struct v4l2_queryctrl *c = arg;
+ err = saa7134_try_fmt_cap(file, priv, f);
+ if (0 != err)
+ return err;
- if ((c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1) &&
- (c->id < V4L2_CID_PRIVATE_BASE ||
- c->id >= V4L2_CID_PRIVATE_LASTP1))
- return -EINVAL;
- ctrl = ctrl_by_id(c->id);
- *c = (NULL != ctrl) ? *ctrl : no_ctrl;
- return 0;
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->cap.field = f->fmt.pix.field;
+ return 0;
+}
+
+static int saa7134_s_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int err;
+ unsigned int flags;
+
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
}
- case VIDIOC_G_CTRL:
- return get_control(dev,arg);
- case VIDIOC_S_CTRL:
- {
- mutex_lock(&dev->lock);
- err = set_control(dev,NULL,arg);
- mutex_unlock(&dev->lock);
+ err = verify_preview(dev, &f->fmt.win);
+ if (0 != err)
return err;
- }
- /* --- input switching --------------------------------------- */
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *i = arg;
- unsigned int n;
- n = i->index;
- if (n >= SAA7134_INPUT_MAX)
- return -EINVAL;
- if (NULL == card_in(dev,i->index).name)
- return -EINVAL;
- memset(i,0,sizeof(*i));
- i->index = n;
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(i->name,card_in(dev,n).name);
- if (card_in(dev,n).tv)
- i->type = V4L2_INPUT_TYPE_TUNER;
- i->audioset = 1;
- if (n == dev->ctl_input) {
- int v1 = saa_readb(SAA7134_STATUS_VIDEO1);
- int v2 = saa_readb(SAA7134_STATUS_VIDEO2);
-
- if (0 != (v1 & 0x40))
- i->status |= V4L2_IN_ST_NO_H_LOCK;
- if (0 != (v2 & 0x40))
- i->status |= V4L2_IN_ST_NO_SYNC;
- if (0 != (v2 & 0x0e))
- i->status |= V4L2_IN_ST_MACROVISION;
- }
- for (n = 0; n < TVNORMS; n++)
- i->std |= tvnorms[n].id;
- return 0;
- }
- case VIDIOC_G_INPUT:
- {
- int *i = arg;
- *i = dev->ctl_input;
- return 0;
- }
- case VIDIOC_S_INPUT:
- {
- int *i = arg;
+ mutex_lock(&dev->lock);
- if (*i < 0 || *i >= SAA7134_INPUT_MAX)
- return -EINVAL;
- if (NULL == card_in(dev,*i).name)
- return -EINVAL;
- mutex_lock(&dev->lock);
- video_mux(dev,*i);
+ fh->win = f->fmt.win;
+ fh->nclips = f->fmt.win.clipcount;
+
+ if (fh->nclips > 8)
+ fh->nclips = 8;
+
+ if (copy_from_user(fh->clips, f->fmt.win.clips,
+ sizeof(struct v4l2_clip)*fh->nclips)) {
mutex_unlock(&dev->lock);
- return 0;
+ return -EFAULT;
}
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev, fh);
+ start_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
}
+
+ mutex_unlock(&dev->lock);
return 0;
}
-EXPORT_SYMBOL(saa7134_common_ioctl);
-/*
- * This function is _not_ called directly, but from
- * video_generic_ioctl (and maybe others). userspace
- * copying is done already, arg is a kernel pointer.
- */
-static int video_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c)
{
- struct saa7134_fh *fh = file->private_data;
+ const struct v4l2_queryctrl *ctrl;
+
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1) &&
+ (c->id < V4L2_CID_PRIVATE_BASE ||
+ c->id >= V4L2_CID_PRIVATE_LASTP1))
+ return -EINVAL;
+ ctrl = ctrl_by_id(c->id);
+ *c = (NULL != ctrl) ? *ctrl : no_ctrl;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7134_queryctrl);
+
+static int saa7134_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= SAA7134_INPUT_MAX)
+ return -EINVAL;
+ if (NULL == card_in(dev, i->index).name)
+ return -EINVAL;
+ memset(i, 0, sizeof(*i));
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, card_in(dev, n).name);
+ if (card_in(dev, n).tv)
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->audioset = 1;
+ if (n == dev->ctl_input) {
+ int v1 = saa_readb(SAA7134_STATUS_VIDEO1);
+ int v2 = saa_readb(SAA7134_STATUS_VIDEO2);
+
+ if (0 != (v1 & 0x40))
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ if (0 != (v2 & 0x40))
+ i->status |= V4L2_IN_ST_NO_SYNC;
+ if (0 != (v2 & 0x0e))
+ i->status |= V4L2_IN_ST_MACROVISION;
+ }
+ i->std = SAA7134_NORMS;
+ return 0;
+}
+
+static int saa7134_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ *i = dev->ctl_input;
+ return 0;
+}
+
+static int saa7134_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct saa7134_fh *fh = priv;
struct saa7134_dev *dev = fh->dev;
- unsigned long flags;
int err;
- if (video_debug > 1)
- v4l_print_ioctl(dev->name,cmd);
-
- switch (cmd) {
- case VIDIOC_S_CTRL:
- case VIDIOC_S_STD:
- case VIDIOC_S_INPUT:
- case VIDIOC_S_TUNER:
- case VIDIOC_S_FREQUENCY:
- err = v4l2_prio_check(&dev->prio,&fh->prio);
- if (0 != err)
- return err;
- }
+ err = v4l2_prio_check(&dev->prio, &fh->prio);
+ if (0 != err)
+ return err;
- switch (cmd) {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
- unsigned int tuner_type = dev->tuner_type;
-
- memset(cap,0,sizeof(*cap));
- strcpy(cap->driver, "saa7134");
- strlcpy(cap->card, saa7134_boards[dev->board].name,
- sizeof(cap->card));
- sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
- cap->version = SAA7134_VERSION_CODE;
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VBI_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_TUNER;
- if (saa7134_no_overlay <= 0) {
- cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
- }
+ if (i < 0 || i >= SAA7134_INPUT_MAX)
+ return -EINVAL;
+ if (NULL == card_in(dev, i).name)
+ return -EINVAL;
+ mutex_lock(&dev->lock);
+ video_mux(dev, i);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
- if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET))
- cap->capabilities &= ~V4L2_CAP_TUNER;
+static int saa7134_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned int tuner_type = dev->tuner_type;
+
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->version = SAA7134_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_TUNER;
+ if (saa7134_no_overlay <= 0)
+ cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
+
+ if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET))
+ cap->capabilities &= ~V4L2_CAP_TUNER;
return 0;
- }
+}
- /* --- tv standards ------------------------------------------ */
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *e = arg;
- unsigned int i;
+static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned long flags;
+ unsigned int i;
+ v4l2_std_id fixup;
+ int err;
- i = e->index;
- if (i >= TVNORMS)
- return -EINVAL;
- err = v4l2_video_std_construct(e, tvnorms[e->index].id,
- tvnorms[e->index].name);
- e->index = i;
- if (err < 0)
- return err;
- return 0;
- }
- case VIDIOC_G_STD:
- {
- v4l2_std_id *id = arg;
+ err = v4l2_prio_check(&dev->prio, &fh->prio);
+ if (0 != err)
+ return err;
- *id = dev->tvnorm->id;
- return 0;
- }
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg;
- unsigned int i;
- v4l2_std_id fixup;
+ for (i = 0; i < TVNORMS; i++)
+ if (*id == tvnorms[i].id)
+ break;
+ if (i == TVNORMS)
for (i = 0; i < TVNORMS; i++)
- if (*id == tvnorms[i].id)
+ if (*id & tvnorms[i].id)
break;
- if (i == TVNORMS)
- for (i = 0; i < TVNORMS; i++)
- if (*id & tvnorms[i].id)
- break;
- if (i == TVNORMS)
- return -EINVAL;
- if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) {
- if (secam[0] == 'L' || secam[0] == 'l') {
- if (secam[1] == 'C' || secam[1] == 'c')
- fixup = V4L2_STD_SECAM_LC;
- else
- fixup = V4L2_STD_SECAM_L;
- } else {
- if (secam[0] == 'D' || secam[0] == 'd')
- fixup = V4L2_STD_SECAM_DK;
- else
- fixup = V4L2_STD_SECAM;
- }
- for (i = 0; i < TVNORMS; i++)
- if (fixup == tvnorms[i].id)
- break;
+ if (i == TVNORMS)
+ return -EINVAL;
+
+ if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) {
+ if (secam[0] == 'L' || secam[0] == 'l') {
+ if (secam[1] == 'C' || secam[1] == 'c')
+ fixup = V4L2_STD_SECAM_LC;
+ else
+ fixup = V4L2_STD_SECAM_L;
+ } else {
+ if (secam[0] == 'D' || secam[0] == 'd')
+ fixup = V4L2_STD_SECAM_DK;
+ else
+ fixup = V4L2_STD_SECAM;
}
- mutex_lock(&dev->lock);
- if (res_check(fh, RESOURCE_OVERLAY)) {
- spin_lock_irqsave(&dev->slock,flags);
- stop_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock, flags);
-
- set_tvnorm(dev,&tvnorms[i]);
-
- spin_lock_irqsave(&dev->slock, flags);
- start_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- } else
- set_tvnorm(dev,&tvnorms[i]);
- saa7134_tvaudio_do_scan(dev);
- mutex_unlock(&dev->lock);
- return 0;
+ for (i = 0; i < TVNORMS; i++)
+ if (fixup == tvnorms[i].id)
+ break;
}
- case VIDIOC_CROPCAP:
- {
- struct v4l2_cropcap *cap = arg;
+ *id = tvnorms[i].id;
- if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
- cap->bounds = dev->crop_bounds;
- cap->defrect = dev->crop_defrect;
- cap->pixelaspect.numerator = 1;
- cap->pixelaspect.denominator = 1;
- if (dev->tvnorm->id & V4L2_STD_525_60) {
- cap->pixelaspect.numerator = 11;
- cap->pixelaspect.denominator = 10;
- }
- if (dev->tvnorm->id & V4L2_STD_625_50) {
- cap->pixelaspect.numerator = 54;
- cap->pixelaspect.denominator = 59;
- }
- return 0;
- }
+ mutex_lock(&dev->lock);
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
- case VIDIOC_G_CROP:
- {
- struct v4l2_crop * crop = arg;
+ set_tvnorm(dev, &tvnorms[i]);
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
- crop->c = dev->crop_current;
- return 0;
- }
- case VIDIOC_S_CROP:
- {
- struct v4l2_crop *crop = arg;
- struct v4l2_rect *b = &dev->crop_bounds;
+ spin_lock_irqsave(&dev->slock, flags);
+ start_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ } else
+ set_tvnorm(dev, &tvnorms[i]);
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
- if (crop->c.height < 0)
- return -EINVAL;
- if (crop->c.width < 0)
- return -EINVAL;
+ saa7134_tvaudio_do_scan(dev);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
- if (res_locked(fh->dev,RESOURCE_OVERLAY))
- return -EBUSY;
- if (res_locked(fh->dev,RESOURCE_VIDEO))
- return -EBUSY;
+static int saa7134_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cap)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
- if (crop->c.top < b->top)
- crop->c.top = b->top;
- if (crop->c.top > b->top + b->height)
- crop->c.top = b->top + b->height;
- if (crop->c.height > b->top - crop->c.top + b->height)
- crop->c.height = b->top - crop->c.top + b->height;
-
- if (crop->c.left < b->left)
- crop->c.left = b->left;
- if (crop->c.left > b->left + b->width)
- crop->c.left = b->left + b->width;
- if (crop->c.width > b->left - crop->c.left + b->width)
- crop->c.width = b->left - crop->c.left + b->width;
-
- dev->crop_current = crop->c;
- return 0;
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ cap->bounds = dev->crop_bounds;
+ cap->defrect = dev->crop_defrect;
+ cap->pixelaspect.numerator = 1;
+ cap->pixelaspect.denominator = 1;
+ if (dev->tvnorm->id & V4L2_STD_525_60) {
+ cap->pixelaspect.numerator = 11;
+ cap->pixelaspect.denominator = 10;
+ }
+ if (dev->tvnorm->id & V4L2_STD_625_50) {
+ cap->pixelaspect.numerator = 54;
+ cap->pixelaspect.denominator = 59;
}
+ return 0;
+}
- /* --- tuner ioctls ------------------------------------------ */
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *t = arg;
- int n;
+static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
- if (0 != t->index)
- return -EINVAL;
- memset(t,0,sizeof(*t));
- for (n = 0; n < SAA7134_INPUT_MAX; n++)
- if (card_in(dev,n).tv)
- break;
- if (NULL != card_in(dev,n).name) {
- strcpy(t->name, "Television");
- t->type = V4L2_TUNER_ANALOG_TV;
- t->capability = V4L2_TUNER_CAP_NORM |
- V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 |
- V4L2_TUNER_CAP_LANG2;
- t->rangehigh = 0xffffffffUL;
- t->rxsubchans = saa7134_tvaudio_getstereo(dev);
- t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans);
- }
- if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03))
- t->signal = 0xffff;
- return 0;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *t = arg;
- int rx,mode;
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ crop->c = dev->crop_current;
+ return 0;
+}
- mode = dev->thread.mode;
- if (UNSET == mode) {
- rx = saa7134_tvaudio_getstereo(dev);
- mode = saa7134_tvaudio_rx2mode(t->rxsubchans);
- }
- if (mode != t->audmode) {
- dev->thread.mode = t->audmode;
- }
- return 0;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+static int saa7134_s_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+ struct v4l2_rect *b = &dev->crop_bounds;
- memset(f,0,sizeof(*f));
- f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- f->frequency = dev->ctl_freq;
- return 0;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ if (crop->c.height < 0)
+ return -EINVAL;
+ if (crop->c.width < 0)
+ return -EINVAL;
- if (0 != f->tuner)
- return -EINVAL;
- if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
- return -EINVAL;
- if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
- return -EINVAL;
- mutex_lock(&dev->lock);
- dev->ctl_freq = f->frequency;
+ if (res_locked(fh->dev, RESOURCE_OVERLAY))
+ return -EBUSY;
+ if (res_locked(fh->dev, RESOURCE_VIDEO))
+ return -EBUSY;
+
+ if (crop->c.top < b->top)
+ crop->c.top = b->top;
+ if (crop->c.top > b->top + b->height)
+ crop->c.top = b->top + b->height;
+ if (crop->c.height > b->top - crop->c.top + b->height)
+ crop->c.height = b->top - crop->c.top + b->height;
+
+ if (crop->c.left < b->left)
+ crop->c.left = b->left;
+ if (crop->c.left > b->left + b->width)
+ crop->c.left = b->left + b->width;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width = b->left - crop->c.left + b->width;
+
+ dev->crop_current = crop->c;
+ return 0;
+}
- saa7134_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,f);
+static int saa7134_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int n;
- saa7134_tvaudio_do_scan(dev);
- mutex_unlock(&dev->lock);
- return 0;
- }
+ if (0 != t->index)
+ return -EINVAL;
+ memset(t, 0, sizeof(*t));
+ for (n = 0; n < SAA7134_INPUT_MAX; n++)
+ if (card_in(dev, n).tv)
+ break;
+ if (NULL != card_in(dev, n).name) {
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM |
+ V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2;
+ t->rangehigh = 0xffffffffUL;
+ t->rxsubchans = saa7134_tvaudio_getstereo(dev);
+ t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans);
+ }
+ if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03))
+ t->signal = 0xffff;
+ return 0;
+}
- /* --- control ioctls ---------------------------------------- */
- case VIDIOC_ENUMINPUT:
- case VIDIOC_G_INPUT:
- case VIDIOC_S_INPUT:
- case VIDIOC_QUERYCTRL:
- case VIDIOC_G_CTRL:
- case VIDIOC_S_CTRL:
- return saa7134_common_ioctl(dev, cmd, arg);
+static int saa7134_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int rx, mode, err;
- case VIDIOC_G_AUDIO:
- {
- struct v4l2_audio *a = arg;
+ err = v4l2_prio_check(&dev->prio, &fh->prio);
+ if (0 != err)
+ return err;
- memset(a,0,sizeof(*a));
- strcpy(a->name,"audio");
- return 0;
- }
- case VIDIOC_S_AUDIO:
- return 0;
- case VIDIOC_G_PARM:
- {
- struct v4l2_captureparm *parm = arg;
- memset(parm,0,sizeof(*parm));
- return 0;
+ mode = dev->thread.mode;
+ if (UNSET == mode) {
+ rx = saa7134_tvaudio_getstereo(dev);
+ mode = saa7134_tvaudio_rx2mode(t->rxsubchans);
}
+ if (mode != t->audmode)
+ dev->thread.mode = t->audmode;
- case VIDIOC_G_PRIORITY:
- {
- enum v4l2_priority *p = arg;
+ return 0;
+}
- *p = v4l2_prio_max(&dev->prio);
- return 0;
- }
- case VIDIOC_S_PRIORITY:
- {
- enum v4l2_priority *prio = arg;
+static int saa7134_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
- return v4l2_prio_change(&dev->prio, &fh->prio, *prio);
- }
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->ctl_freq;
- /* --- preview ioctls ---------------------------------------- */
- case VIDIOC_ENUM_FMT:
- {
- struct v4l2_fmtdesc *f = arg;
- enum v4l2_buf_type type;
- unsigned int index;
-
- index = f->index;
- type = f->type;
- switch (type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (saa7134_no_overlay > 0) {
- printk ("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
- if (index >= FORMATS)
- return -EINVAL;
- if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY &&
- formats[index].planar)
- return -EINVAL;
- memset(f,0,sizeof(*f));
- f->index = index;
- f->type = type;
- strlcpy(f->description,formats[index].name,sizeof(f->description));
- f->pixelformat = formats[index].fourcc;
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (0 != index)
- return -EINVAL;
- memset(f,0,sizeof(*f));
- f->index = index;
- f->type = type;
- f->pixelformat = V4L2_PIX_FMT_GREY;
- strcpy(f->description,"vbi data");
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
- case VIDIOC_G_FBUF:
- {
- struct v4l2_framebuffer *fb = arg;
+ return 0;
+}
- *fb = dev->ovbuf;
- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
- return 0;
- }
- case VIDIOC_S_FBUF:
- {
- struct v4l2_framebuffer *fb = arg;
- struct saa7134_format *fmt;
+static int saa7134_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int err;
- if(!capable(CAP_SYS_ADMIN) &&
- !capable(CAP_SYS_RAWIO))
- return -EPERM;
+ err = v4l2_prio_check(&dev->prio, &fh->prio);
+ if (0 != err)
+ return err;
- /* check args */
- fmt = format_by_fourcc(fb->fmt.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
+ if (0 != f->tuner)
+ return -EINVAL;
+ if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
+ return -EINVAL;
+ if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
+ return -EINVAL;
+ mutex_lock(&dev->lock);
+ dev->ctl_freq = f->frequency;
- /* ok, accept it */
- dev->ovbuf = *fb;
- dev->ovfmt = fmt;
- if (0 == dev->ovbuf.fmt.bytesperline)
- dev->ovbuf.fmt.bytesperline =
- dev->ovbuf.fmt.width*fmt->depth/8;
- return 0;
+ saa7134_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
+
+ saa7134_tvaudio_do_scan(dev);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int saa7134_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ strcpy(a->name, "audio");
+ return 0;
+}
+
+static int saa7134_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int saa7134_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ *p = v4l2_prio_max(&dev->prio);
+ return 0;
+}
+
+static int saa7134_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ return v4l2_prio_change(&dev->prio, &fh->prio, prio);
+}
+
+static int saa7134_enum_fmt_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= FORMATS)
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int saa7134_enum_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
}
- case VIDIOC_OVERLAY:
- {
- int *on = arg;
- if (*on) {
- if (saa7134_no_overlay > 0) {
- printk ("no_overlay\n");
- return -EINVAL;
- }
+ if ((f->index >= FORMATS) || formats[f->index].planar)
+ return -EINVAL;
- if (!res_get(dev,fh,RESOURCE_OVERLAY))
- return -EBUSY;
- spin_lock_irqsave(&dev->slock,flags);
- start_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- }
- if (!*on) {
- if (!res_check(fh, RESOURCE_OVERLAY))
- return -EINVAL;
- spin_lock_irqsave(&dev->slock,flags);
- stop_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- res_free(dev,fh,RESOURCE_OVERLAY);
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int saa7134_enum_fmt_vbi(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (0 != f->index)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_PIX_FMT_GREY;
+ strcpy(f->description, "vbi data");
+
+ return 0;
+}
+
+static int saa7134_g_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ *fb = dev->ovbuf;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+
+ return 0;
+}
+
+static int saa7134_s_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_format *fmt;
+
+ if (!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ /* ok, accept it */
+ dev->ovbuf = *fb;
+ dev->ovfmt = fmt;
+ if (0 == dev->ovbuf.fmt.bytesperline)
+ dev->ovbuf.fmt.bytesperline =
+ dev->ovbuf.fmt.width*fmt->depth/8;
+ return 0;
+}
+
+static int saa7134_overlay(struct file *file, void *f, unsigned int on)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned long flags;
+
+ if (on) {
+ if (saa7134_no_overlay > 0) {
+ dprintk("no_overlay\n");
+ return -EINVAL;
}
- return 0;
- }
- /* --- capture ioctls ---------------------------------------- */
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *f = arg;
- return saa7134_g_fmt(dev,fh,f);
- }
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *f = arg;
- return saa7134_s_fmt(dev,fh,f);
+ if (!res_get(dev, fh, RESOURCE_OVERLAY))
+ return -EBUSY;
+ spin_lock_irqsave(&dev->slock, flags);
+ start_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
}
- case VIDIOC_TRY_FMT:
- {
- struct v4l2_format *f = arg;
- return saa7134_try_fmt(dev,fh,f);
+ if (!on) {
+ if (!res_check(fh, RESOURCE_OVERLAY))
+ return -EINVAL;
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ res_free(dev, fh, RESOURCE_OVERLAY);
}
+ return 0;
+}
+
#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCGMBUF:
- return videobuf_cgmbuf(saa7134_queue(fh), arg, gbuffers);
+static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
+{
+ struct saa7134_fh *fh = file->private_data;
+ return videobuf_cgmbuf(saa7134_queue(fh), mbuf, 8);
+}
#endif
- case VIDIOC_REQBUFS:
- return videobuf_reqbufs(saa7134_queue(fh),arg);
- case VIDIOC_QUERYBUF:
- return videobuf_querybuf(saa7134_queue(fh),arg);
+static int saa7134_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_reqbufs(saa7134_queue(fh), p);
+}
- case VIDIOC_QBUF:
- return videobuf_qbuf(saa7134_queue(fh),arg);
+static int saa7134_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_querybuf(saa7134_queue(fh), b);
+}
- case VIDIOC_DQBUF:
- return videobuf_dqbuf(saa7134_queue(fh),arg,
- file->f_flags & O_NONBLOCK);
+static int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_qbuf(saa7134_queue(fh), b);
+}
- case VIDIOC_STREAMON:
- {
- int res = saa7134_resource(fh);
+static int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_dqbuf(saa7134_queue(fh), b,
+ file->f_flags & O_NONBLOCK);
+}
- if (!res_get(dev,fh,res))
- return -EBUSY;
- return videobuf_streamon(saa7134_queue(fh));
- }
- case VIDIOC_STREAMOFF:
- {
- int res = saa7134_resource(fh);
+static int saa7134_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int res = saa7134_resource(fh);
- err = videobuf_streamoff(saa7134_queue(fh));
- if (err < 0)
- return err;
- res_free(dev,fh,res);
- return 0;
- }
+ if (!res_get(dev, fh, res))
+ return -EBUSY;
- default:
- return v4l_compat_translate_ioctl(inode,file,cmd,arg,
- video_do_ioctl);
- }
+ return videobuf_streamon(saa7134_queue(fh));
+}
+
+static int saa7134_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ int err;
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int res = saa7134_resource(fh);
+
+ err = videobuf_streamoff(saa7134_queue(fh));
+ if (err < 0)
+ return err;
+ res_free(dev, fh, res);
return 0;
}
-static int video_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int saa7134_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
{
- return video_usercopy(inode, file, cmd, arg, video_do_ioctl);
+ return 0;
}
-static int radio_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
{
struct saa7134_fh *fh = file->private_data;
struct saa7134_dev *dev = fh->dev;
- if (video_debug > 1)
- v4l_print_ioctl(dev->name,cmd);
- switch (cmd) {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
-
- memset(cap,0,sizeof(*cap));
- strcpy(cap->driver, "saa7134");
- strlcpy(cap->card, saa7134_boards[dev->board].name,
- sizeof(cap->card));
- sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
- cap->version = SAA7134_VERSION_CODE;
- cap->capabilities = V4L2_CAP_TUNER;
- return 0;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *t = arg;
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->version = SAA7134_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
- if (0 != t->index)
- return -EINVAL;
+static int radio_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
- memset(t,0,sizeof(*t));
- strcpy(t->name, "Radio");
- t->type = V4L2_TUNER_RADIO;
+ if (0 != t->index)
+ return -EINVAL;
- saa7134_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
- if (dev->input->amux == TV) {
- t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11);
- t->rxsubchans = (saa_readb(0x529) & 0x08) ?
- V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
- }
- return 0;
+ memset(t, 0, sizeof(*t));
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ saa7134_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+ if (dev->input->amux == TV) {
+ t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11);
+ t->rxsubchans = (saa_readb(0x529) & 0x08) ?
+ V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
}
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *t = arg;
+ return 0;
+}
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
- if (0 != t->index)
- return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
- saa7134_i2c_call_clients(dev,VIDIOC_S_TUNER,t);
+ saa7134_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
+ return 0;
+}
- return 0;
- }
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *i = arg;
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
- if (i->index != 0)
- return -EINVAL;
- strcpy(i->name,"Radio");
- i->type = V4L2_INPUT_TYPE_TUNER;
- return 0;
- }
- case VIDIOC_G_INPUT:
- {
- int *i = arg;
- *i = 0;
- return 0;
- }
- case VIDIOC_G_AUDIO:
- {
- struct v4l2_audio *a = arg;
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
- memset(a,0,sizeof(*a));
- strcpy(a->name,"Radio");
- return 0;
- }
- case VIDIOC_G_STD:
- {
- v4l2_std_id *id = arg;
- *id = 0;
- return 0;
- }
- case VIDIOC_S_AUDIO:
- case VIDIOC_S_INPUT:
- case VIDIOC_S_STD:
- return 0;
+ return 0;
+}
- case VIDIOC_QUERYCTRL:
- {
- const struct v4l2_queryctrl *ctrl;
- struct v4l2_queryctrl *c = arg;
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
- if (c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1)
- return -EINVAL;
- if (c->id == V4L2_CID_AUDIO_MUTE) {
- ctrl = ctrl_by_id(c->id);
- *c = *ctrl;
- } else
- *c = no_ctrl;
- return 0;
- }
+static int radio_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ memset(a, 0, sizeof(*a));
+ strcpy(a->name, "Radio");
+ return 0;
+}
- case VIDIOC_G_CTRL:
- case VIDIOC_S_CTRL:
- case VIDIOC_G_FREQUENCY:
- case VIDIOC_S_FREQUENCY:
- return video_do_ioctl(inode,file,cmd,arg);
+static int radio_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
- default:
- return v4l_compat_translate_ioctl(inode,file,cmd,arg,
- radio_do_ioctl);
- }
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
return 0;
}
-static int radio_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
{
- return video_usercopy(inode, file, cmd, arg, radio_do_ioctl);
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ const struct v4l2_queryctrl *ctrl;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl = ctrl_by_id(c->id);
+ *c = *ctrl;
+ } else
+ *c = no_ctrl;
+ return 0;
}
static const struct file_operations video_fops =
@@ -2333,7 +2327,7 @@ static const struct file_operations video_fops =
.read = video_read,
.poll = video_poll,
.mmap = video_mmap,
- .ioctl = video_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -2343,7 +2337,7 @@ static const struct file_operations radio_fops =
.owner = THIS_MODULE,
.open = video_open,
.release = video_release,
- .ioctl = radio_ioctl,
+ .ioctl = video_ioctl2,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -2353,27 +2347,79 @@ static const struct file_operations radio_fops =
struct video_device saa7134_video_template =
{
- .name = "saa7134-video",
- .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|
- VID_TYPE_CLIPPING|VID_TYPE_SCALES,
- .fops = &video_fops,
- .minor = -1,
-};
-
-struct video_device saa7134_vbi_template =
-{
- .name = "saa7134-vbi",
- .type = VID_TYPE_TUNER|VID_TYPE_TELETEXT,
- .fops = &video_fops,
- .minor = -1,
+ .name = "saa7134-video",
+ .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER |
+ VID_TYPE_CLIPPING|VID_TYPE_SCALES,
+ .fops = &video_fops,
+ .minor = -1,
+ .vidioc_querycap = saa7134_querycap,
+ .vidioc_enum_fmt_cap = saa7134_enum_fmt_cap,
+ .vidioc_g_fmt_cap = saa7134_g_fmt_cap,
+ .vidioc_try_fmt_cap = saa7134_try_fmt_cap,
+ .vidioc_s_fmt_cap = saa7134_s_fmt_cap,
+ .vidioc_enum_fmt_overlay = saa7134_enum_fmt_overlay,
+ .vidioc_g_fmt_overlay = saa7134_g_fmt_overlay,
+ .vidioc_try_fmt_overlay = saa7134_try_fmt_overlay,
+ .vidioc_s_fmt_overlay = saa7134_s_fmt_overlay,
+ .vidioc_enum_fmt_vbi = saa7134_enum_fmt_vbi,
+ .vidioc_g_fmt_vbi = saa7134_try_get_set_fmt_vbi,
+ .vidioc_try_fmt_vbi = saa7134_try_get_set_fmt_vbi,
+ .vidioc_s_fmt_vbi = saa7134_try_get_set_fmt_vbi,
+ .vidioc_g_audio = saa7134_g_audio,
+ .vidioc_s_audio = saa7134_s_audio,
+ .vidioc_cropcap = saa7134_cropcap,
+ .vidioc_reqbufs = saa7134_reqbufs,
+ .vidioc_querybuf = saa7134_querybuf,
+ .vidioc_qbuf = saa7134_qbuf,
+ .vidioc_dqbuf = saa7134_dqbuf,
+ .vidioc_s_std = saa7134_s_std,
+ .vidioc_enum_input = saa7134_enum_input,
+ .vidioc_g_input = saa7134_g_input,
+ .vidioc_s_input = saa7134_s_input,
+ .vidioc_queryctrl = saa7134_queryctrl,
+ .vidioc_g_ctrl = saa7134_g_ctrl,
+ .vidioc_s_ctrl = saa7134_s_ctrl,
+ .vidioc_streamon = saa7134_streamon,
+ .vidioc_streamoff = saa7134_streamoff,
+ .vidioc_g_tuner = saa7134_g_tuner,
+ .vidioc_s_tuner = saa7134_s_tuner,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ .vidiocgmbuf = vidiocgmbuf,
+#endif
+ .vidioc_g_crop = saa7134_g_crop,
+ .vidioc_s_crop = saa7134_s_crop,
+ .vidioc_g_fbuf = saa7134_g_fbuf,
+ .vidioc_s_fbuf = saa7134_s_fbuf,
+ .vidioc_overlay = saa7134_overlay,
+ .vidioc_g_priority = saa7134_g_priority,
+ .vidioc_s_priority = saa7134_s_priority,
+ .vidioc_g_parm = saa7134_g_parm,
+ .vidioc_g_frequency = saa7134_g_frequency,
+ .vidioc_s_frequency = saa7134_s_frequency,
+ .tvnorms = SAA7134_NORMS,
+ .current_norm = V4L2_STD_PAL,
};
struct video_device saa7134_radio_template =
{
- .name = "saa7134-radio",
- .type = VID_TYPE_TUNER,
- .fops = &radio_fops,
- .minor = -1,
+ .name = "saa7134-radio",
+ .type = VID_TYPE_TUNER,
+ .fops = &radio_fops,
+ .minor = -1,
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_s_std = radio_s_std,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_input = radio_g_input,
+ .vidioc_g_ctrl = saa7134_g_ctrl,
+ .vidioc_s_ctrl = saa7134_s_ctrl,
+ .vidioc_g_frequency = saa7134_g_frequency,
+ .vidioc_s_frequency = saa7134_s_frequency,
};
int saa7134_video_init1(struct saa7134_dev *dev)
@@ -2511,7 +2557,7 @@ void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status)
goto done;
}
dev->video_q.curr->vb.field_count = dev->video_fieldcount;
- saa7134_buffer_finish(dev,&dev->video_q,STATE_DONE);
+ saa7134_buffer_finish(dev,&dev->video_q,VIDEOBUF_DONE);
}
saa7134_buffer_next(dev,&dev->video_q);
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 66a390c321a7..ce450304fb53 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -240,6 +240,19 @@ struct saa7134_format {
#define SAA7134_BOARD_SABRENT_TV_PCB05 115
#define SAA7134_BOARD_10MOONSTVMASTER3 116
#define SAA7134_BOARD_AVERMEDIA_SUPER_007 117
+#define SAA7134_BOARD_BEHOLD_401 118
+#define SAA7134_BOARD_BEHOLD_403 119
+#define SAA7134_BOARD_BEHOLD_403FM 120
+#define SAA7134_BOARD_BEHOLD_405 121
+#define SAA7134_BOARD_BEHOLD_405FM 122
+#define SAA7134_BOARD_BEHOLD_407 123
+#define SAA7134_BOARD_BEHOLD_407FM 124
+#define SAA7134_BOARD_BEHOLD_409 125
+#define SAA7134_BOARD_BEHOLD_505FM 126
+#define SAA7134_BOARD_BEHOLD_507_9FM 127
+#define SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM 128
+#define SAA7134_BOARD_BEHOLD_607_9FM 129
+#define SAA7134_BOARD_BEHOLD_M6 130
#define SAA7134_MAXBOARDS 8
#define SAA7134_INPUT_MAX 8
@@ -481,7 +494,7 @@ struct saa7134_dev {
/* i2c i/o */
struct i2c_adapter i2c_adap;
struct i2c_client i2c_client;
- unsigned char eedata[128];
+ unsigned char eedata[256];
/* video overlay */
struct v4l2_framebuffer ovbuf;
@@ -566,6 +579,12 @@ struct saa7134_dev {
#define saa_wait(us) { udelay(us); }
+#define SAA7134_NORMS (\
+ V4L2_STD_PAL | V4L2_STD_PAL_N | \
+ V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \
+ V4L2_STD_NTSC | V4L2_STD_PAL_M | \
+ V4L2_STD_PAL_60)
+
/* ----------------------------------------------------------- */
/* saa7134-core.c */
@@ -596,9 +615,6 @@ void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q);
void saa7134_buffer_timeout(unsigned long data);
void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf);
-int saa7134_buffer_requeue(struct saa7134_dev *dev,
- struct saa7134_dmaqueue *q);
-
int saa7134_set_dmabits(struct saa7134_dev *dev);
extern int (*saa7134_dmasound_init)(struct saa7134_dev *dev);
@@ -628,16 +644,17 @@ void saa7134_i2c_call_clients(struct saa7134_dev *dev,
/* ----------------------------------------------------------- */
/* saa7134-video.c */
+extern unsigned int video_debug;
extern struct video_device saa7134_video_template;
extern struct video_device saa7134_radio_template;
-void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm);
+int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c);
+int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c);
+int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c);
+
int saa7134_videoport_init(struct saa7134_dev *dev);
void saa7134_set_tvnorm_hw(struct saa7134_dev *dev);
-int saa7134_common_ioctl(struct saa7134_dev *dev,
- unsigned int cmd, void *arg);
-
int saa7134_video_init1(struct saa7134_dev *dev);
int saa7134_video_init2(struct saa7134_dev *dev);
void saa7134_irq_video_signalchange(struct saa7134_dev *dev);
@@ -682,6 +699,7 @@ void saa7134_tvaudio_setinput(struct saa7134_dev *dev,
void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level);
int saa7134_tvaudio_getstereo(struct saa7134_dev *dev);
+void saa7134_tvaudio_init(struct saa7134_dev *dev);
int saa7134_tvaudio_init2(struct saa7134_dev *dev);
int saa7134_tvaudio_fini(struct saa7134_dev *dev);
int saa7134_tvaudio_do_scan(struct saa7134_dev *dev);
diff --git a/drivers/media/video/sn9c102/Makefile b/drivers/media/video/sn9c102/Makefile
index a56d16f69c71..7ecd5a90c7c9 100644
--- a/drivers/media/video/sn9c102/Makefile
+++ b/drivers/media/video/sn9c102/Makefile
@@ -3,6 +3,7 @@ sn9c102-objs := sn9c102_core.o \
sn9c102_hv7131r.o \
sn9c102_mi0343.o \
sn9c102_mi0360.o \
+ sn9c102_mt9v111.o \
sn9c102_ov7630.o \
sn9c102_ov7660.o \
sn9c102_pas106b.o \
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
index 511847912c48..c40ba3adab21 100644
--- a/drivers/media/video/sn9c102/sn9c102_core.c
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -47,7 +47,7 @@
#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia"
#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define SN9C102_MODULE_LICENSE "GPL"
-#define SN9C102_MODULE_VERSION "1:1.47"
+#define SN9C102_MODULE_VERSION "1:1.47pre49"
#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 47)
/*****************************************************************************/
@@ -3322,7 +3322,6 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
cam->v4ldev->fops = &sn9c102_fops;
cam->v4ldev->minor = video_nr[dev_nr];
cam->v4ldev->release = video_device_release;
- video_set_drvdata(cam->v4ldev, cam);
init_completion(&cam->probe);
@@ -3340,6 +3339,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
+ video_set_drvdata(cam->v4ldev, cam);
cam->module_param.force_munmap = force_munmap[dev_nr];
cam->module_param.frame_timeout = frame_timeout[dev_nr];
diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h
index 916054faf9be..35223e0d7e49 100644
--- a/drivers/media/video/sn9c102/sn9c102_devtable.h
+++ b/drivers/media/video/sn9c102/sn9c102_devtable.h
@@ -126,6 +126,7 @@ extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam);
extern int sn9c102_probe_hv7131r(struct sn9c102_device* cam);
extern int sn9c102_probe_mi0343(struct sn9c102_device* cam);
extern int sn9c102_probe_mi0360(struct sn9c102_device* cam);
+extern int sn9c102_probe_mt9v111(struct sn9c102_device *cam);
extern int sn9c102_probe_ov7630(struct sn9c102_device* cam);
extern int sn9c102_probe_ov7660(struct sn9c102_device* cam);
extern int sn9c102_probe_pas106b(struct sn9c102_device* cam);
@@ -144,6 +145,7 @@ static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = {
&sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */
&sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */
&sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */
&sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */
&sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */
&sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */
diff --git a/drivers/media/video/sn9c102/sn9c102_mt9v111.c b/drivers/media/video/sn9c102/sn9c102_mt9v111.c
new file mode 100644
index 000000000000..3b98ac3bbc38
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_mt9v111.c
@@ -0,0 +1,259 @@
+/***************************************************************************
+ * Plug-in for MT9V111 image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+
+
+static int mt9v111_init(struct sn9c102_device *cam)
+{
+ struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
+ int err = 0;
+
+ err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02},
+ {0x00, 0x03}, {0x1a, 0x04},
+ {0x1f, 0x05}, {0x20, 0x06},
+ {0x1f, 0x07}, {0x81, 0x08},
+ {0x5c, 0x09}, {0x00, 0x0a},
+ {0x00, 0x0b}, {0x00, 0x0c},
+ {0x00, 0x0d}, {0x00, 0x0e},
+ {0x00, 0x0f}, {0x03, 0x10},
+ {0x00, 0x11}, {0x00, 0x12},
+ {0x02, 0x13}, {0x14, 0x14},
+ {0x28, 0x15}, {0x1e, 0x16},
+ {0xe2, 0x17}, {0x06, 0x18},
+ {0x00, 0x19}, {0x00, 0x1a},
+ {0x00, 0x1b}, {0x08, 0x20},
+ {0x39, 0x21}, {0x51, 0x22},
+ {0x63, 0x23}, {0x73, 0x24},
+ {0x82, 0x25}, {0x8f, 0x26},
+ {0x9b, 0x27}, {0xa7, 0x28},
+ {0xb1, 0x29}, {0xbc, 0x2a},
+ {0xc6, 0x2b}, {0xcf, 0x2c},
+ {0xd8, 0x2d}, {0xe1, 0x2e},
+ {0xea, 0x2f}, {0xf2, 0x30},
+ {0x13, 0x84}, {0x00, 0x85},
+ {0x25, 0x86}, {0x00, 0x87},
+ {0x07, 0x88}, {0x00, 0x89},
+ {0xee, 0x8a}, {0x0f, 0x8b},
+ {0xe5, 0x8c}, {0x0f, 0x8d},
+ {0x2e, 0x8e}, {0x00, 0x8f},
+ {0x30, 0x90}, {0x00, 0x91},
+ {0xd4, 0x92}, {0x0f, 0x93},
+ {0xfc, 0x94}, {0x0f, 0x95},
+ {0x14, 0x96}, {0x00, 0x97},
+ {0x00, 0x98}, {0x60, 0x99},
+ {0x07, 0x9a}, {0x40, 0x9b},
+ {0x20, 0x9c}, {0x00, 0x9d},
+ {0x00, 0x9e}, {0x00, 0x9f},
+ {0x2d, 0xc0}, {0x2d, 0xc1},
+ {0x3a, 0xc2}, {0x05, 0xc3},
+ {0x04, 0xc4}, {0x3f, 0xc5},
+ {0x00, 0xc6}, {0x00, 0xc7},
+ {0x50, 0xc8}, {0x3c, 0xc9},
+ {0x28, 0xca}, {0xd8, 0xcb},
+ {0x14, 0xcc}, {0xec, 0xcd},
+ {0x32, 0xce}, {0xdd, 0xcf},
+ {0x2d, 0xd0}, {0xdd, 0xd1},
+ {0x6a, 0xd2}, {0x50, 0xd3},
+ {0x60, 0xd4}, {0x00, 0xd5},
+ {0x00, 0xd6});
+
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01,
+ 0x00, 0x01, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+ 0x00, 0x01, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08,
+ 0x04, 0x80, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01,
+ 0x00, 0x04, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08,
+ 0x00, 0x08, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x02,
+ 0x00, 0x16, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03,
+ 0x01, 0xe7, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04,
+ 0x02, 0x87, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06,
+ 0x00, 0x40, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05,
+ 0x00, 0x09, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x07,
+ 0x30, 0x02, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0c,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x12,
+ 0x00, 0xb0, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x13,
+ 0x00, 0x7c, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x1e,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01,
+ 0x00, 0x04, 0, 0);
+
+ return err;
+}
+
+static int mt9v111_get_ctrl(struct sn9c102_device *cam,
+ struct v4l2_control *ctrl)
+{
+ struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
+ u8 data[2];
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2,
+ data) < 0)
+ return -EIO;
+ ctrl->value = data[1] & 0x80 ? 1 : 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+static int mt9v111_set_ctrl(struct sn9c102_device *cam,
+ const struct v4l2_control *ctrl)
+{
+ struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x20,
+ ctrl->value ? 0x80 : 0x00,
+ ctrl->value ? 0x80 : 0x00, 0,
+ 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+static int mt9v111_set_crop(struct sn9c102_device *cam,
+ const struct v4l2_rect *rect)
+{
+ struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 v_start = (u8) (rect->top - s->cropcap.bounds.top) + 2;
+
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+static int mt9v111_set_pix_format(struct sn9c102_device *cam,
+ const struct v4l2_pix_format *pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
+ err += sn9c102_write_reg(cam, 0xb4, 0x17);
+ } else {
+ err += sn9c102_write_reg(cam, 0xe2, 0x17);
+ }
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor mt9v111 = {
+ .name = "MT9V111",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x5c,
+ .init = &mt9v111_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &mt9v111_get_ctrl,
+ .set_ctrl = &mt9v111_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &mt9v111_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &mt9v111_set_pix_format
+};
+
+
+int sn9c102_probe_mt9v111(struct sn9c102_device *cam)
+{
+ u8 data[2];
+ int err = 0;
+
+ err += sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1},
+ {0x29, 0x01}, {0x42, 0x17},
+ {0x62, 0x17}, {0x08, 0x01});
+ err += sn9c102_i2c_try_raw_write(cam, &mt9v111, 4,
+ mt9v111.i2c_slave_id, 0x01, 0x00,
+ 0x04, 0, 0);
+ if (err || sn9c102_i2c_try_raw_read(cam, &mt9v111,
+ mt9v111.i2c_slave_id, 0x36, 2,
+ data) < 0)
+ return -EIO;
+
+ if (data[0] != 0x82 || data[1] != 0x3a)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &mt9v111);
+
+ return 0;
+}
diff --git a/drivers/media/video/stk-sensor.c b/drivers/media/video/stk-sensor.c
new file mode 100644
index 000000000000..4a9a0b62efa3
--- /dev/null
+++ b/drivers/media/video/stk-sensor.c
@@ -0,0 +1,578 @@
+/* stk-sensor.c: Driver for ov96xx sensor (used in some Syntek webcams)
+ *
+ * Copyright 2007-2008 Jaime Velasco Juan <jsagarribay@gmail.com>
+ *
+ * Some parts derived from ov7670.c:
+ * Copyright 2006 One Laptop Per Child Association, Inc. Written
+ * by Jonathan Corbet with substantial inspiration from Mark
+ * McClelland's ovcamchip code.
+ *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ *
+ * This file may be distributed under the terms of the GNU General
+ * 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.
+ */
+
+/* Controlling the sensor via the STK1125 vendor specific control interface:
+ * The camera uses an OmniVision sensor and the stk1125 provides an
+ * SCCB(i2c)-USB bridge which let us program the sensor.
+ * In my case the sensor id is 0x9652, it can be read from sensor's register
+ * 0x0A and 0x0B as follows:
+ * - read register #R:
+ * output #R to index 0x0208
+ * output 0x0070 to index 0x0200
+ * input 1 byte from index 0x0201 (some kind of status register)
+ * until its value is 0x01
+ * input 1 byte from index 0x0209. This is the value of #R
+ * - write value V to register #R
+ * output #R to index 0x0204
+ * output V to index 0x0205
+ * output 0x0005 to index 0x0200
+ * input 1 byte from index 0x0201 until its value becomes 0x04
+ */
+
+/* It seems the i2c bus is controlled with these registers */
+
+#include "stk-webcam.h"
+
+#define STK_IIC_BASE (0x0200)
+# define STK_IIC_OP (STK_IIC_BASE)
+# define STK_IIC_OP_TX (0x05)
+# define STK_IIC_OP_RX (0x70)
+# define STK_IIC_STAT (STK_IIC_BASE+1)
+# define STK_IIC_STAT_TX_OK (0x04)
+# define STK_IIC_STAT_RX_OK (0x01)
+/* I don't know what does this register.
+ * when it is 0x00 or 0x01, we cannot talk to the sensor,
+ * other values work */
+# define STK_IIC_ENABLE (STK_IIC_BASE+2)
+# define STK_IIC_ENABLE_NO (0x00)
+/* This is what the driver writes in windows */
+# define STK_IIC_ENABLE_YES (0x1e)
+/*
+ * Address of the slave. Seems like the binary driver look for the
+ * sensor in multiple places, attempting a reset sequence.
+ * We only know about the ov9650
+ */
+# define STK_IIC_ADDR (STK_IIC_BASE+3)
+# define STK_IIC_TX_INDEX (STK_IIC_BASE+4)
+# define STK_IIC_TX_VALUE (STK_IIC_BASE+5)
+# define STK_IIC_RX_INDEX (STK_IIC_BASE+8)
+# define STK_IIC_RX_VALUE (STK_IIC_BASE+9)
+
+#define MAX_RETRIES (50)
+
+#define SENSOR_ADDRESS (0x60)
+
+/* From ov7670.c (These registers aren't fully accurate) */
+
+/* Registers */
+#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */
+#define REG_BLUE 0x01 /* blue gain */
+#define REG_RED 0x02 /* red gain */
+#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */
+#define REG_COM1 0x04 /* Control 1 */
+#define COM1_CCIR656 0x40 /* CCIR656 enable */
+#define COM1_QFMT 0x20 /* QVGA/QCIF format */
+#define COM1_SKIP_0 0x00 /* Do not skip any row */
+#define COM1_SKIP_2 0x04 /* Skip 2 rows of 4 */
+#define COM1_SKIP_3 0x08 /* Skip 3 rows of 4 */
+#define REG_BAVE 0x05 /* U/B Average level */
+#define REG_GbAVE 0x06 /* Y/Gb Average level */
+#define REG_AECHH 0x07 /* AEC MS 5 bits */
+#define REG_RAVE 0x08 /* V/R Average level */
+#define REG_COM2 0x09 /* Control 2 */
+#define COM2_SSLEEP 0x10 /* Soft sleep mode */
+#define REG_PID 0x0a /* Product ID MSB */
+#define REG_VER 0x0b /* Product ID LSB */
+#define REG_COM3 0x0c /* Control 3 */
+#define COM3_SWAP 0x40 /* Byte swap */
+#define COM3_SCALEEN 0x08 /* Enable scaling */
+#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */
+#define REG_COM4 0x0d /* Control 4 */
+#define REG_COM5 0x0e /* All "reserved" */
+#define REG_COM6 0x0f /* Control 6 */
+#define REG_AECH 0x10 /* More bits of AEC value */
+#define REG_CLKRC 0x11 /* Clock control */
+#define CLK_PLL 0x80 /* Enable internal PLL */
+#define CLK_EXT 0x40 /* Use external clock directly */
+#define CLK_SCALE 0x3f /* Mask for internal clock scale */
+#define REG_COM7 0x12 /* Control 7 */
+#define COM7_RESET 0x80 /* Register reset */
+#define COM7_FMT_MASK 0x38
+#define COM7_FMT_SXGA 0x00
+#define COM7_FMT_VGA 0x40
+#define COM7_FMT_CIF 0x20 /* CIF format */
+#define COM7_FMT_QVGA 0x10 /* QVGA format */
+#define COM7_FMT_QCIF 0x08 /* QCIF format */
+#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */
+#define COM7_YUV 0x00 /* YUV */
+#define COM7_BAYER 0x01 /* Bayer format */
+#define COM7_PBAYER 0x05 /* "Processed bayer" */
+#define REG_COM8 0x13 /* Control 8 */
+#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */
+#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */
+#define COM8_BFILT 0x20 /* Band filter enable */
+#define COM8_AGC 0x04 /* Auto gain enable */
+#define COM8_AWB 0x02 /* White balance enable */
+#define COM8_AEC 0x01 /* Auto exposure enable */
+#define REG_COM9 0x14 /* Control 9 - gain ceiling */
+#define REG_COM10 0x15 /* Control 10 */
+#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */
+#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */
+#define COM10_HREF_REV 0x08 /* Reverse HREF */
+#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */
+#define COM10_VS_NEG 0x02 /* VSYNC negative */
+#define COM10_HS_NEG 0x01 /* HSYNC negative */
+#define REG_HSTART 0x17 /* Horiz start high bits */
+#define REG_HSTOP 0x18 /* Horiz stop high bits */
+#define REG_VSTART 0x19 /* Vert start high bits */
+#define REG_VSTOP 0x1a /* Vert stop high bits */
+#define REG_PSHFT 0x1b /* Pixel delay after HREF */
+#define REG_MIDH 0x1c /* Manuf. ID high */
+#define REG_MIDL 0x1d /* Manuf. ID low */
+#define REG_MVFP 0x1e /* Mirror / vflip */
+#define MVFP_MIRROR 0x20 /* Mirror image */
+#define MVFP_FLIP 0x10 /* Vertical flip */
+
+#define REG_AEW 0x24 /* AGC upper limit */
+#define REG_AEB 0x25 /* AGC lower limit */
+#define REG_VPT 0x26 /* AGC/AEC fast mode op region */
+#define REG_ADVFL 0x2d /* Insert dummy lines (LSB) */
+#define REG_ADVFH 0x2e /* Insert dummy lines (MSB) */
+#define REG_HSYST 0x30 /* HSYNC rising edge delay */
+#define REG_HSYEN 0x31 /* HSYNC falling edge delay */
+#define REG_HREF 0x32 /* HREF pieces */
+#define REG_TSLB 0x3a /* lots of stuff */
+#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */
+#define TSLB_BYTEORD 0x08 /* swap bytes in 16bit mode? */
+#define REG_COM11 0x3b /* Control 11 */
+#define COM11_NIGHT 0x80 /* NIght mode enable */
+#define COM11_NMFR 0x60 /* Two bit NM frame rate */
+#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */
+#define COM11_50HZ 0x08 /* Manual 50Hz select */
+#define COM11_EXP 0x02
+#define REG_COM12 0x3c /* Control 12 */
+#define COM12_HREF 0x80 /* HREF always */
+#define REG_COM13 0x3d /* Control 13 */
+#define COM13_GAMMA 0x80 /* Gamma enable */
+#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */
+#define COM13_CMATRIX 0x10 /* Enable color matrix for RGB or YUV */
+#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */
+#define REG_COM14 0x3e /* Control 14 */
+#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */
+#define REG_EDGE 0x3f /* Edge enhancement factor */
+#define REG_COM15 0x40 /* Control 15 */
+#define COM15_R10F0 0x00 /* Data range 10 to F0 */
+#define COM15_R01FE 0x80 /* 01 to FE */
+#define COM15_R00FF 0xc0 /* 00 to FF */
+#define COM15_RGB565 0x10 /* RGB565 output */
+#define COM15_RGBFIXME 0x20 /* FIXME */
+#define COM15_RGB555 0x30 /* RGB555 output */
+#define REG_COM16 0x41 /* Control 16 */
+#define COM16_AWBGAIN 0x08 /* AWB gain enable */
+#define REG_COM17 0x42 /* Control 17 */
+#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */
+#define COM17_CBAR 0x08 /* DSP Color bar */
+
+/*
+ * This matrix defines how the colors are generated, must be
+ * tweaked to adjust hue and saturation.
+ *
+ * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
+ *
+ * They are nine-bit signed quantities, with the sign bit
+ * stored in 0x58. Sign for v-red is bit 0, and up from there.
+ */
+#define REG_CMATRIX_BASE 0x4f
+#define CMATRIX_LEN 6
+#define REG_CMATRIX_SIGN 0x58
+
+
+#define REG_BRIGHT 0x55 /* Brightness */
+#define REG_CONTRAS 0x56 /* Contrast control */
+
+#define REG_GFIX 0x69 /* Fix gain control */
+
+#define REG_RGB444 0x8c /* RGB 444 control */
+#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */
+#define R444_RGBX 0x01 /* Empty nibble at end */
+
+#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */
+#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */
+
+#define REG_BD50MAX 0xa5 /* 50hz banding step limit */
+#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */
+#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */
+#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */
+#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */
+#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */
+#define REG_BD60MAX 0xab /* 60hz banding step limit */
+
+
+
+
+/* Returns 0 if OK */
+int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val)
+{
+ int i = 0;
+ int tmpval = 0;
+
+ if (stk_camera_write_reg(dev, STK_IIC_TX_INDEX, reg))
+ return 1;
+ if (stk_camera_write_reg(dev, STK_IIC_TX_VALUE, val))
+ return 1;
+ if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_TX))
+ return 1;
+ do {
+ if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval))
+ return 1;
+ i++;
+ } while (tmpval == 0 && i < MAX_RETRIES);
+ if (tmpval != STK_IIC_STAT_TX_OK) {
+ if (tmpval)
+ STK_ERROR("stk_sensor_outb failed, status=0x%02x\n",
+ tmpval);
+ return 1;
+ } else
+ return 0;
+}
+
+int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val)
+{
+ int i = 0;
+ int tmpval = 0;
+
+ if (stk_camera_write_reg(dev, STK_IIC_RX_INDEX, reg))
+ return 1;
+ if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_RX))
+ return 1;
+ do {
+ if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval))
+ return 1;
+ i++;
+ } while (tmpval == 0 && i < MAX_RETRIES);
+ if (tmpval != STK_IIC_STAT_RX_OK) {
+ if (tmpval)
+ STK_ERROR("stk_sensor_inb failed, status=0x%02x\n",
+ tmpval);
+ return 1;
+ }
+
+ if (stk_camera_read_reg(dev, STK_IIC_RX_VALUE, &tmpval))
+ return 1;
+
+ *val = (u8) tmpval;
+ return 0;
+}
+
+static int stk_sensor_write_regvals(struct stk_camera *dev,
+ struct regval *rv)
+{
+ int ret;
+ if (rv == NULL)
+ return 0;
+ while (rv->reg != 0xff || rv->val != 0xff) {
+ ret = stk_sensor_outb(dev, rv->reg, rv->val);
+ if (ret != 0)
+ return ret;
+ rv++;
+ }
+ return 0;
+}
+
+int stk_sensor_sleep(struct stk_camera *dev)
+{
+ u8 tmp;
+ return stk_sensor_inb(dev, REG_COM2, &tmp)
+ || stk_sensor_outb(dev, REG_COM2, tmp|COM2_SSLEEP);
+}
+
+int stk_sensor_wakeup(struct stk_camera *dev)
+{
+ u8 tmp;
+ return stk_sensor_inb(dev, REG_COM2, &tmp)
+ || stk_sensor_outb(dev, REG_COM2, tmp&~COM2_SSLEEP);
+}
+
+static struct regval ov_initvals[] = {
+ {REG_CLKRC, CLK_PLL},
+ {REG_COM11, 0x01},
+ {0x6a, 0x7d},
+ {REG_AECH, 0x40},
+ {REG_GAIN, 0x00},
+ {REG_BLUE, 0x80},
+ {REG_RED, 0x80},
+ /* Do not enable fast AEC for now */
+ /*{REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC},*/
+ {REG_COM8, COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC},
+ {0x39, 0x50}, {0x38, 0x93},
+ {0x37, 0x00}, {0x35, 0x81},
+ {REG_COM5, 0x20},
+ {REG_COM1, 0x00},
+ {REG_COM3, 0x00},
+ {REG_COM4, 0x00},
+ {REG_PSHFT, 0x00},
+ {0x16, 0x07},
+ {0x33, 0xe2}, {0x34, 0xbf},
+ {REG_COM16, 0x00},
+ {0x96, 0x04},
+ /* Gamma curve values */
+/* { 0x7a, 0x20 }, { 0x7b, 0x10 },
+ { 0x7c, 0x1e }, { 0x7d, 0x35 },
+ { 0x7e, 0x5a }, { 0x7f, 0x69 },
+ { 0x80, 0x76 }, { 0x81, 0x80 },
+ { 0x82, 0x88 }, { 0x83, 0x8f },
+ { 0x84, 0x96 }, { 0x85, 0xa3 },
+ { 0x86, 0xaf }, { 0x87, 0xc4 },
+ { 0x88, 0xd7 }, { 0x89, 0xe8 },
+*/
+ {REG_GFIX, 0x40},
+ {0x8e, 0x00},
+ {REG_COM12, 0x73},
+ {0x8f, 0xdf}, {0x8b, 0x06},
+ {0x8c, 0x20},
+ {0x94, 0x88}, {0x95, 0x88},
+/* {REG_COM15, 0xc1}, TODO */
+ {0x29, 0x3f},
+ {REG_COM6, 0x42},
+ {REG_BD50MAX, 0x80},
+ {REG_HAECC6, 0xb8}, {REG_HAECC7, 0x92},
+ {REG_BD60MAX, 0x0a},
+ {0x90, 0x00}, {0x91, 0x00},
+ {REG_HAECC1, 0x00}, {REG_HAECC2, 0x00},
+ {REG_AEW, 0x68}, {REG_AEB, 0x5c},
+ {REG_VPT, 0xc3},
+ {REG_COM9, 0x2e},
+ {0x2a, 0x00}, {0x2b, 0x00},
+
+ {0xff, 0xff}, /* END MARKER */
+};
+
+/* Probe the I2C bus and initialise the sensor chip */
+int stk_sensor_init(struct stk_camera *dev)
+{
+ u8 idl = 0;
+ u8 idh = 0;
+
+ if (stk_camera_write_reg(dev, STK_IIC_ENABLE, STK_IIC_ENABLE_YES)
+ || stk_camera_write_reg(dev, STK_IIC_ADDR, SENSOR_ADDRESS)
+ || stk_sensor_outb(dev, REG_COM7, COM7_RESET)) {
+ STK_ERROR("Sensor resetting failed\n");
+ return -ENODEV;
+ }
+ msleep(10);
+ /* Read the manufacturer ID: ov = 0x7FA2 */
+ if (stk_sensor_inb(dev, REG_MIDH, &idh)
+ || stk_sensor_inb(dev, REG_MIDL, &idl)) {
+ STK_ERROR("Strange error reading sensor ID\n");
+ return -ENODEV;
+ }
+ if (idh != 0x7F || idl != 0xA2) {
+ STK_ERROR("Huh? you don't have a sensor from ovt\n");
+ return -ENODEV;
+ }
+ if (stk_sensor_inb(dev, REG_PID, &idh)
+ || stk_sensor_inb(dev, REG_VER, &idl)) {
+ STK_ERROR("Could not read sensor model\n");
+ return -ENODEV;
+ }
+ stk_sensor_write_regvals(dev, ov_initvals);
+ msleep(10);
+ STK_INFO("OmniVision sensor detected, id %02X%02X"
+ " at address %x\n", idh, idl, SENSOR_ADDRESS);
+ return 0;
+}
+
+/* V4L2_PIX_FMT_UYVY */
+static struct regval ov_fmt_uyvy[] = {
+ {REG_TSLB, TSLB_YLAST|0x08 },
+ { 0x4f, 0x80 }, /* "matrix coefficient 1" */
+ { 0x50, 0x80 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x22 }, /* "matrix coefficient 4" */
+ { 0x53, 0x5e }, /* "matrix coefficient 5" */
+ { 0x54, 0x80 }, /* "matrix coefficient 6" */
+ {REG_COM13, COM13_UVSAT|COM13_CMATRIX},
+ {REG_COM15, COM15_R00FF },
+ {0xff, 0xff}, /* END MARKER */
+};
+
+/* V4L2_PIX_FMT_RGB565X rrrrrggg gggbbbbb */
+static struct regval ov_fmt_rgbr[] = {
+ { REG_RGB444, 0 }, /* No RGB444 please */
+ {REG_TSLB, 0x00},
+ { REG_COM1, 0x0 },
+ { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */
+ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */
+ { 0x50, 0xb3 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x3d }, /* "matrix coefficient 4" */
+ { 0x53, 0xa7 }, /* "matrix coefficient 5" */
+ { 0x54, 0xe4 }, /* "matrix coefficient 6" */
+ { REG_COM13, COM13_GAMMA },
+ { REG_COM15, COM15_RGB565|COM15_R00FF },
+ { 0xff, 0xff },
+};
+
+/* V4L2_PIX_FMT_RGB565 gggbbbbb rrrrrggg */
+static struct regval ov_fmt_rgbp[] = {
+ { REG_RGB444, 0 }, /* No RGB444 please */
+ {REG_TSLB, TSLB_BYTEORD },
+ { REG_COM1, 0x0 },
+ { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */
+ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */
+ { 0x50, 0xb3 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x3d }, /* "matrix coefficient 4" */
+ { 0x53, 0xa7 }, /* "matrix coefficient 5" */
+ { 0x54, 0xe4 }, /* "matrix coefficient 6" */
+ { REG_COM13, COM13_GAMMA },
+ { REG_COM15, COM15_RGB565|COM15_R00FF },
+ { 0xff, 0xff },
+};
+
+/* V4L2_PIX_FMT_SRGGB8 */
+static struct regval ov_fmt_bayer[] = {
+ /* This changes color order */
+ {REG_TSLB, 0x40}, /* BGGR */
+ /* {REG_TSLB, 0x08}, */ /* BGGR with vertical image flipping */
+ {REG_COM15, COM15_R00FF },
+ {0xff, 0xff}, /* END MARKER */
+};
+/*
+ * Store a set of start/stop values into the camera.
+ */
+static int stk_sensor_set_hw(struct stk_camera *dev,
+ int hstart, int hstop, int vstart, int vstop)
+{
+ int ret;
+ unsigned char v;
+/*
+ * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of
+ * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is
+ * a mystery "edge offset" value in the top two bits of href.
+ */
+ ret = stk_sensor_outb(dev, REG_HSTART, (hstart >> 3) & 0xff);
+ ret += stk_sensor_outb(dev, REG_HSTOP, (hstop >> 3) & 0xff);
+ ret += stk_sensor_inb(dev, REG_HREF, &v);
+ v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7);
+ msleep(10);
+ ret += stk_sensor_outb(dev, REG_HREF, v);
+/*
+ * Vertical: similar arrangement (note: this is different from ov7670.c)
+ */
+ ret += stk_sensor_outb(dev, REG_VSTART, (vstart >> 3) & 0xff);
+ ret += stk_sensor_outb(dev, REG_VSTOP, (vstop >> 3) & 0xff);
+ ret += stk_sensor_inb(dev, REG_VREF, &v);
+ v = (v & 0xc0) | ((vstop & 0x7) << 3) | (vstart & 0x7);
+ msleep(10);
+ ret += stk_sensor_outb(dev, REG_VREF, v);
+ return ret;
+}
+
+
+int stk_sensor_configure(struct stk_camera *dev)
+{
+ int com7;
+ /*
+ * We setup the sensor to output dummy lines in low-res modes,
+ * so we don't get absurdly hight framerates.
+ */
+ unsigned dummylines;
+ int flip;
+ struct regval *rv;
+
+ switch (dev->vsettings.mode) {
+ case MODE_QCIF: com7 = COM7_FMT_QCIF;
+ dummylines = 604;
+ break;
+ case MODE_QVGA: com7 = COM7_FMT_QVGA;
+ dummylines = 267;
+ break;
+ case MODE_CIF: com7 = COM7_FMT_CIF;
+ dummylines = 412;
+ break;
+ case MODE_VGA: com7 = COM7_FMT_VGA;
+ dummylines = 11;
+ break;
+ case MODE_SXGA: com7 = COM7_FMT_SXGA;
+ dummylines = 0;
+ break;
+ default: STK_ERROR("Unsupported mode %d\n", dev->vsettings.mode);
+ return -EFAULT;
+ }
+ switch (dev->vsettings.palette) {
+ case V4L2_PIX_FMT_UYVY:
+ com7 |= COM7_YUV;
+ rv = ov_fmt_uyvy;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ com7 |= COM7_RGB;
+ rv = ov_fmt_rgbp;
+ break;
+ case V4L2_PIX_FMT_RGB565X:
+ com7 |= COM7_RGB;
+ rv = ov_fmt_rgbr;
+ break;
+ case V4L2_PIX_FMT_SBGGR8:
+ com7 |= COM7_PBAYER;
+ rv = ov_fmt_bayer;
+ break;
+ default: STK_ERROR("Unsupported colorspace\n");
+ return -EFAULT;
+ }
+ /*FIXME sometimes the sensor go to a bad state
+ stk_sensor_write_regvals(dev, ov_initvals); */
+ stk_sensor_outb(dev, REG_COM7, com7);
+ msleep(50);
+ stk_sensor_write_regvals(dev, rv);
+ flip = (dev->vsettings.vflip?MVFP_FLIP:0)
+ | (dev->vsettings.hflip?MVFP_MIRROR:0);
+ stk_sensor_outb(dev, REG_MVFP, flip);
+ if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8
+ && !dev->vsettings.vflip)
+ stk_sensor_outb(dev, REG_TSLB, 0x08);
+ stk_sensor_outb(dev, REG_ADVFH, dummylines >> 8);
+ stk_sensor_outb(dev, REG_ADVFL, dummylines & 0xff);
+ msleep(50);
+ switch (dev->vsettings.mode) {
+ case MODE_VGA:
+ if (stk_sensor_set_hw(dev, 302, 1582, 6, 486))
+ STK_ERROR("stk_sensor_set_hw failed (VGA)\n");
+ break;
+ case MODE_SXGA:
+ case MODE_CIF:
+ case MODE_QVGA:
+ case MODE_QCIF:
+ /*FIXME These settings seem ignored by the sensor
+ if (stk_sensor_set_hw(dev, 220, 1500, 10, 1034))
+ STK_ERROR("stk_sensor_set_hw failed (SXGA)\n");
+ */
+ break;
+ }
+ msleep(10);
+ return 0;
+}
+
+int stk_sensor_set_brightness(struct stk_camera *dev, int br)
+{
+ if (br < 0 || br > 0xff)
+ return -EINVAL;
+ stk_sensor_outb(dev, REG_AEB, max(0x00, br - 6));
+ stk_sensor_outb(dev, REG_AEW, min(0xff, br + 6));
+ return 0;
+}
+
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
new file mode 100644
index 000000000000..d37e5e2594b4
--- /dev/null
+++ b/drivers/media/video/stk-webcam.c
@@ -0,0 +1,1465 @@
+/*
+ * stk-webcam.c : Driver for Syntek 1125 USB webcam controller
+ *
+ * Copyright (C) 2006 Nicolas VIVIEN
+ * Copyright 2007-2008 Jaime Velasco Juan <jsagarribay@gmail.com>
+ *
+ * Some parts are inspired from cafe_ccic.c
+ * Copyright 2006-2007 Jonathan Corbet
+ *
+ * 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
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/kref.h>
+
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+
+#include "stk-webcam.h"
+
+
+static int hflip = 1;
+module_param(hflip, bool, 0444);
+MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 1");
+
+static int vflip = 1;
+module_param(vflip, bool, 0444);
+MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 1");
+
+static int debug;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Debug v4l ioctls. Defaults to 0");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jaime Velasco Juan <jsagarribay@gmail.com> and Nicolas VIVIEN");
+MODULE_DESCRIPTION("Syntek DC1125 webcam driver");
+
+
+
+/* Some cameras have audio interfaces, we aren't interested in those */
+static struct usb_device_id stkwebcam_table[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(0x174f, 0xa311, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x05e1, 0x0501, 0xff, 0xff, 0xff) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, stkwebcam_table);
+
+void stk_camera_cleanup(struct kref *kref)
+{
+ struct stk_camera *dev = to_stk_camera(kref);
+
+ STK_INFO("Syntek USB2.0 Camera release resources"
+ " video device /dev/video%d\n", dev->vdev.minor);
+ video_unregister_device(&dev->vdev);
+ dev->vdev.priv = NULL;
+
+ if (dev->sio_bufs != NULL || dev->isobufs != NULL)
+ STK_ERROR("We are leaking memory\n");
+ usb_put_intf(dev->interface);
+ kfree(dev);
+}
+
+
+/*
+ * Basic stuff
+ */
+int stk_camera_write_reg(struct stk_camera *dev, u16 index, u8 value)
+{
+ struct usb_device *udev = dev->udev;
+ int ret;
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ index,
+ NULL,
+ 0,
+ 500);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+int stk_camera_read_reg(struct stk_camera *dev, u16 index, int *value)
+{
+ struct usb_device *udev = dev->udev;
+ int ret;
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00,
+ index,
+ (u8 *) value,
+ sizeof(u8),
+ 500);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+static int stk_start_stream(struct stk_camera *dev)
+{
+ int value;
+ int i, ret;
+ int value_116, value_117;
+
+ if (!is_present(dev))
+ return -ENODEV;
+ if (!is_memallocd(dev) || !is_initialised(dev)) {
+ STK_ERROR("FIXME: Buffers are not allocated\n");
+ return -EFAULT;
+ }
+ ret = usb_set_interface(dev->udev, 0, 5);
+
+ if (ret < 0)
+ STK_ERROR("usb_set_interface failed !\n");
+ if (stk_sensor_wakeup(dev))
+ STK_ERROR("error awaking the sensor\n");
+
+ stk_camera_read_reg(dev, 0x0116, &value_116);
+ stk_camera_read_reg(dev, 0x0117, &value_117);
+
+ stk_camera_write_reg(dev, 0x0116, 0x0000);
+ stk_camera_write_reg(dev, 0x0117, 0x0000);
+
+ stk_camera_read_reg(dev, 0x0100, &value);
+ stk_camera_write_reg(dev, 0x0100, value | 0x80);
+
+ stk_camera_write_reg(dev, 0x0116, value_116);
+ stk_camera_write_reg(dev, 0x0117, value_117);
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ if (dev->isobufs[i].urb) {
+ ret = usb_submit_urb(dev->isobufs[i].urb, GFP_KERNEL);
+ atomic_inc(&dev->urbs_used);
+ if (ret)
+ return ret;
+ }
+ }
+ set_streaming(dev);
+ return 0;
+}
+
+static int stk_stop_stream(struct stk_camera *dev)
+{
+ int value;
+ int i;
+ if (is_present(dev)) {
+ stk_camera_read_reg(dev, 0x0100, &value);
+ stk_camera_write_reg(dev, 0x0100, value & ~0x80);
+ if (dev->isobufs != NULL) {
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ if (dev->isobufs[i].urb)
+ usb_kill_urb(dev->isobufs[i].urb);
+ }
+ }
+ unset_streaming(dev);
+
+ if (usb_set_interface(dev->udev, 0, 0))
+ STK_ERROR("usb_set_interface failed !\n");
+ if (stk_sensor_sleep(dev))
+ STK_ERROR("error suspending the sensor\n");
+ }
+ return 0;
+}
+
+/*
+ * This seems to be the shortest init sequence we
+ * must do in order to find the sensor
+ * Bit 5 of reg. 0x0000 here is important, when reset to 0 the sensor
+ * is also reset. Maybe powers down it?
+ * Rest of values don't make a difference
+ */
+
+static struct regval stk1125_initvals[] = {
+ /*TODO: What means this sequence? */
+ {0x0000, 0x24},
+ {0x0100, 0x21},
+ {0x0002, 0x68},
+ {0x0003, 0x80},
+ {0x0005, 0x00},
+ {0x0007, 0x03},
+ {0x000d, 0x00},
+ {0x000f, 0x02},
+ {0x0300, 0x12},
+ {0x0350, 0x41},
+ {0x0351, 0x00},
+ {0x0352, 0x00},
+ {0x0353, 0x00},
+ {0x0018, 0x10},
+ {0x0019, 0x00},
+ {0x001b, 0x0e},
+ {0x001c, 0x46},
+ {0x0300, 0x80},
+ {0x001a, 0x04},
+ {0x0110, 0x00},
+ {0x0111, 0x00},
+ {0x0112, 0x00},
+ {0x0113, 0x00},
+
+ {0xffff, 0xff},
+};
+
+
+static int stk_initialise(struct stk_camera *dev)
+{
+ struct regval *rv;
+ int ret;
+ if (!is_present(dev))
+ return -ENODEV;
+ if (is_initialised(dev))
+ return 0;
+ rv = stk1125_initvals;
+ while (rv->reg != 0xffff) {
+ ret = stk_camera_write_reg(dev, rv->reg, rv->val);
+ if (ret)
+ return ret;
+ rv++;
+ }
+ if (stk_sensor_init(dev) == 0) {
+ set_initialised(dev);
+ return 0;
+ } else
+ return -1;
+}
+
+/* sysfs functions */
+/*FIXME cleanup this */
+
+static ssize_t show_brightness(struct device *class,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev = to_video_device(class);
+ struct stk_camera *dev = vdev_to_camera(vdev);
+
+ return sprintf(buf, "%X\n", dev->vsettings.brightness);
+}
+
+static ssize_t store_brightness(struct device *class,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char *endp;
+ unsigned long value;
+ int ret;
+
+ struct video_device *vdev = to_video_device(class);
+ struct stk_camera *dev = vdev_to_camera(vdev);
+
+ value = simple_strtoul(buf, &endp, 16);
+
+ dev->vsettings.brightness = (int) value;
+
+ ret = stk_sensor_set_brightness(dev, value >> 8);
+ if (ret)
+ return ret;
+ else
+ return count;
+}
+
+static ssize_t show_hflip(struct device *class,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev = to_video_device(class);
+ struct stk_camera *dev = vdev_to_camera(vdev);
+
+ return sprintf(buf, "%d\n", dev->vsettings.hflip);
+}
+
+static ssize_t store_hflip(struct device *class,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct video_device *vdev = to_video_device(class);
+ struct stk_camera *dev = vdev_to_camera(vdev);
+
+ if (strncmp(buf, "1", 1) == 0)
+ dev->vsettings.hflip = 1;
+ else if (strncmp(buf, "0", 1) == 0)
+ dev->vsettings.hflip = 0;
+ else
+ return -EINVAL;
+
+ return strlen(buf);
+}
+
+static ssize_t show_vflip(struct device *class,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev = to_video_device(class);
+ struct stk_camera *dev = vdev_to_camera(vdev);
+
+ return sprintf(buf, "%d\n", dev->vsettings.vflip);
+}
+
+static ssize_t store_vflip(struct device *class,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct video_device *vdev = to_video_device(class);
+ struct stk_camera *dev = vdev_to_camera(vdev);
+
+ if (strncmp(buf, "1", 1) == 0)
+ dev->vsettings.vflip = 1;
+ else if (strncmp(buf, "0", 1) == 0)
+ dev->vsettings.vflip = 0;
+ else
+ return -EINVAL;
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(brightness, S_IRUGO | S_IWUGO,
+ show_brightness, store_brightness);
+static DEVICE_ATTR(hflip, S_IRUGO | S_IWUGO, show_hflip, store_hflip);
+static DEVICE_ATTR(vflip, S_IRUGO | S_IWUGO, show_vflip, store_vflip);
+
+static int stk_create_sysfs_files(struct video_device *vdev)
+{
+ int ret;
+
+ ret = video_device_create_file(vdev, &dev_attr_brightness);
+ ret += video_device_create_file(vdev, &dev_attr_hflip);
+ ret += video_device_create_file(vdev, &dev_attr_vflip);
+ return ret;
+}
+
+static void stk_remove_sysfs_files(struct video_device *vdev)
+{
+ video_device_remove_file(vdev, &dev_attr_brightness);
+ video_device_remove_file(vdev, &dev_attr_hflip);
+ video_device_remove_file(vdev, &dev_attr_vflip);
+}
+
+
+/* *********************************************** */
+/*
+ * This function is called as an URB transfert is complete (Isochronous pipe).
+ * So, the traitement is done in interrupt time, so it has be fast, not crash,
+ * and not stall. Neat.
+ */
+static void stk_isoc_handler(struct urb *urb)
+{
+ int i;
+ int ret;
+ int framelen;
+ unsigned long flags;
+
+ unsigned char *fill = NULL;
+ unsigned char *iso_buf = NULL;
+
+ struct stk_camera *dev;
+ struct stk_sio_buffer *fb;
+
+ dev = (struct stk_camera *) urb->context;
+
+ if (dev == NULL) {
+ STK_ERROR("isoc_handler called with NULL device !\n");
+ return;
+ }
+
+ if (urb->status == -ENOENT || urb->status == -ECONNRESET
+ || urb->status == -ESHUTDOWN) {
+ atomic_dec(&dev->urbs_used);
+ return;
+ }
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ if (urb->status != -EINPROGRESS && urb->status != 0) {
+ STK_ERROR("isoc_handler: urb->status == %d\n", urb->status);
+ goto resubmit;
+ }
+
+ if (list_empty(&dev->sio_avail)) {
+ /*FIXME Stop streaming after a while */
+ (void) (printk_ratelimit() &&
+ STK_ERROR("isoc_handler without available buffer!\n"));
+ goto resubmit;
+ }
+ fb = list_first_entry(&dev->sio_avail,
+ struct stk_sio_buffer, list);
+ fill = fb->buffer + fb->v4lbuf.bytesused;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ if (urb->iso_frame_desc[i].status != 0) {
+ if (urb->iso_frame_desc[i].status != -EXDEV)
+ STK_ERROR("Frame %d has error %d\n", i,
+ urb->iso_frame_desc[i].status);
+ continue;
+ }
+ framelen = urb->iso_frame_desc[i].actual_length;
+ iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ if (framelen <= 4)
+ continue; /* no data */
+
+ /*
+ * we found something informational from there
+ * the isoc frames have to type of headers
+ * type1: 00 xx 00 00 or 20 xx 00 00
+ * type2: 80 xx 00 00 00 00 00 00 or a0 xx 00 00 00 00 00 00
+ * xx is a sequencer which has never been seen over 0x3f
+ * imho data written down looks like bayer, i see similarities
+ * after every 640 bytes
+ */
+ if (*iso_buf & 0x80) {
+ framelen -= 8;
+ iso_buf += 8;
+ /* This marks a new frame */
+ if (fb->v4lbuf.bytesused != 0
+ && fb->v4lbuf.bytesused != dev->frame_size) {
+ (void) (printk_ratelimit() &&
+ STK_ERROR("frame %d, "
+ "bytesused=%d, skipping\n",
+ i, fb->v4lbuf.bytesused));
+ fb->v4lbuf.bytesused = 0;
+ fill = fb->buffer;
+ } else if (fb->v4lbuf.bytesused == dev->frame_size) {
+ list_move_tail(dev->sio_avail.next,
+ &dev->sio_full);
+ wake_up(&dev->wait_frame);
+ if (list_empty(&dev->sio_avail)) {
+ (void) (printk_ratelimit() &&
+ STK_ERROR("No buffer available\n"));
+ goto resubmit;
+ }
+ fb = list_first_entry(&dev->sio_avail,
+ struct stk_sio_buffer, list);
+ fb->v4lbuf.bytesused = 0;
+ fill = fb->buffer;
+ }
+ } else {
+ framelen -= 4;
+ iso_buf += 4;
+ }
+
+ /* Our buffer is full !!! */
+ if (framelen + fb->v4lbuf.bytesused > dev->frame_size) {
+ (void) (printk_ratelimit() &&
+ STK_ERROR("Frame buffer overflow, lost sync\n"));
+ /*FIXME Do something here? */
+ continue;
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ memcpy(fill, iso_buf, framelen);
+ spin_lock_irqsave(&dev->spinlock, flags);
+ fill += framelen;
+
+ /* New size of our buffer */
+ fb->v4lbuf.bytesused += framelen;
+ }
+
+resubmit:
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ urb->dev = dev->udev;
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret != 0) {
+ STK_ERROR("Error (%d) re-submitting urb in stk_isoc_handler.\n",
+ ret);
+ }
+}
+
+/* -------------------------------------------- */
+
+static int stk_prepare_iso(struct stk_camera *dev)
+{
+ void *kbuf;
+ int i, j;
+ struct urb *urb;
+ struct usb_device *udev;
+
+ if (dev == NULL)
+ return -ENXIO;
+ udev = dev->udev;
+
+ if (dev->isobufs)
+ STK_ERROR("isobufs already allocated. Bad\n");
+ else
+ dev->isobufs = kzalloc(MAX_ISO_BUFS * sizeof(*dev->isobufs),
+ GFP_KERNEL);
+ if (dev->isobufs == NULL) {
+ STK_ERROR("Unable to allocate iso buffers\n");
+ return -ENOMEM;
+ }
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ if (dev->isobufs[i].data == NULL) {
+ kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
+ if (kbuf == NULL) {
+ STK_ERROR("Failed to allocate iso buffer %d\n",
+ i);
+ goto isobufs_out;
+ }
+ dev->isobufs[i].data = kbuf;
+ } else
+ STK_ERROR("isobuf data already allocated\n");
+ if (dev->isobufs[i].urb == NULL) {
+ urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
+ if (urb == NULL) {
+ STK_ERROR("Failed to allocate URB %d\n", i);
+ goto isobufs_out;
+ }
+ dev->isobufs[i].urb = urb;
+ } else {
+ STK_ERROR("Killing URB\n");
+ usb_kill_urb(dev->isobufs[i].urb);
+ urb = dev->isobufs[i].urb;
+ }
+ urb->interval = 1;
+ urb->dev = udev;
+ urb->pipe = usb_rcvisocpipe(udev, dev->isoc_ep);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = dev->isobufs[i].data;
+ urb->transfer_buffer_length = ISO_BUFFER_SIZE;
+ urb->complete = stk_isoc_handler;
+ urb->context = dev;
+ urb->start_frame = 0;
+ urb->number_of_packets = ISO_FRAMES_PER_DESC;
+
+ for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
+ urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
+ urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE;
+ }
+ }
+ set_memallocd(dev);
+ return 0;
+
+isobufs_out:
+ for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].data; i++)
+ kfree(dev->isobufs[i].data);
+ for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].urb; i++)
+ usb_free_urb(dev->isobufs[i].urb);
+ kfree(dev->isobufs);
+ dev->isobufs = NULL;
+ return -ENOMEM;
+}
+
+static void stk_clean_iso(struct stk_camera *dev)
+{
+ int i;
+
+ if (dev == NULL || dev->isobufs == NULL)
+ return;
+
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ struct urb *urb;
+
+ urb = dev->isobufs[i].urb;
+ if (urb) {
+ if (atomic_read(&dev->urbs_used))
+ usb_kill_urb(urb);
+ usb_free_urb(urb);
+ }
+ kfree(dev->isobufs[i].data);
+ }
+ kfree(dev->isobufs);
+ dev->isobufs = NULL;
+ unset_memallocd(dev);
+}
+
+static int stk_setup_siobuf(struct stk_camera *dev, int index)
+{
+ struct stk_sio_buffer *buf = dev->sio_bufs + index;
+ INIT_LIST_HEAD(&buf->list);
+ buf->v4lbuf.length = PAGE_ALIGN(dev->frame_size);
+ buf->buffer = vmalloc_user(buf->v4lbuf.length);
+ if (buf->buffer == NULL)
+ return -ENOMEM;
+ buf->mapcount = 0;
+ buf->dev = dev;
+ buf->v4lbuf.index = index;
+ buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf->v4lbuf.field = V4L2_FIELD_NONE;
+ buf->v4lbuf.memory = V4L2_MEMORY_MMAP;
+ buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length;
+ return 0;
+}
+
+static int stk_free_sio_buffers(struct stk_camera *dev)
+{
+ int i;
+ int nbufs;
+ unsigned long flags;
+ if (dev->n_sbufs == 0 || dev->sio_bufs == NULL)
+ return 0;
+ /*
+ * If any buffers are mapped, we cannot free them at all.
+ */
+ for (i = 0; i < dev->n_sbufs; i++) {
+ if (dev->sio_bufs[i].mapcount > 0)
+ return -EBUSY;
+ }
+ /*
+ * OK, let's do it.
+ */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ INIT_LIST_HEAD(&dev->sio_avail);
+ INIT_LIST_HEAD(&dev->sio_full);
+ nbufs = dev->n_sbufs;
+ dev->n_sbufs = 0;
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ for (i = 0; i < nbufs; i++) {
+ if (dev->sio_bufs[i].buffer != NULL)
+ vfree(dev->sio_bufs[i].buffer);
+ }
+ kfree(dev->sio_bufs);
+ dev->sio_bufs = NULL;
+ return 0;
+}
+
+static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs)
+{
+ int i;
+ if (dev->sio_bufs != NULL)
+ STK_ERROR("sio_bufs already allocated\n");
+ else {
+ dev->sio_bufs = kzalloc(n_sbufs * sizeof(struct stk_sio_buffer),
+ GFP_KERNEL);
+ if (dev->sio_bufs == NULL)
+ return -ENOMEM;
+ for (i = 0; i < n_sbufs; i++) {
+ if (stk_setup_siobuf(dev, i))
+ return (dev->n_sbufs > 1)? 0 : -ENOMEM;
+ dev->n_sbufs = i+1;
+ }
+ }
+ return 0;
+}
+
+static int stk_allocate_buffers(struct stk_camera *dev, unsigned n_sbufs)
+{
+ int err;
+ err = stk_prepare_iso(dev);
+ if (err) {
+ stk_clean_iso(dev);
+ return err;
+ }
+ err = stk_prepare_sio_buffers(dev, n_sbufs);
+ if (err) {
+ stk_free_sio_buffers(dev);
+ return err;
+ }
+ return 0;
+}
+
+static void stk_free_buffers(struct stk_camera *dev)
+{
+ stk_clean_iso(dev);
+ stk_free_sio_buffers(dev);
+}
+/* -------------------------------------------- */
+
+/* v4l file operations */
+
+static int v4l_stk_open(struct inode *inode, struct file *fp)
+{
+ struct stk_camera *dev;
+ struct video_device *vdev;
+
+ vdev = video_devdata(fp);
+ dev = vdev_to_camera(vdev);
+
+ if (dev == NULL || !is_present(dev))
+ return -ENXIO;
+ fp->private_data = vdev;
+ kref_get(&dev->kref);
+
+ return 0;
+}
+
+static int v4l_stk_release(struct inode *inode, struct file *fp)
+{
+ struct stk_camera *dev;
+ struct video_device *vdev;
+
+ vdev = video_devdata(fp);
+ if (vdev == NULL) {
+ STK_ERROR("v4l_release called w/o video devdata\n");
+ return -EFAULT;
+ }
+ dev = vdev_to_camera(vdev);
+ if (dev == NULL) {
+ STK_ERROR("v4l_release called on removed device\n");
+ return -ENODEV;
+ }
+
+ if (dev->owner != fp) {
+ kref_put(&dev->kref, stk_camera_cleanup);
+ return 0;
+ }
+
+ stk_stop_stream(dev);
+
+ stk_free_buffers(dev);
+
+ dev->owner = NULL;
+
+ kref_put(&dev->kref, stk_camera_cleanup);
+
+ return 0;
+}
+
+static ssize_t v4l_stk_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ int i;
+ int ret;
+ unsigned long flags;
+ struct stk_camera *dev;
+ struct video_device *vdev;
+ struct stk_sio_buffer *sbuf;
+
+ vdev = video_devdata(fp);
+ if (vdev == NULL)
+ return -EFAULT;
+ dev = vdev_to_camera(vdev);
+
+ if (dev == NULL)
+ return -EIO;
+
+ if (!is_present(dev))
+ return -EIO;
+ if (dev->owner && dev->owner != fp)
+ return -EBUSY;
+ dev->owner = fp;
+ if (!is_streaming(dev)) {
+ if (stk_initialise(dev)
+ || stk_allocate_buffers(dev, 3)
+ || stk_start_stream(dev))
+ return -ENOMEM;
+ spin_lock_irqsave(&dev->spinlock, flags);
+ for (i = 0; i < dev->n_sbufs; i++) {
+ list_add_tail(&dev->sio_bufs[i].list, &dev->sio_avail);
+ dev->sio_bufs[i].v4lbuf.flags = V4L2_BUF_FLAG_QUEUED;
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ }
+ if (*f_pos == 0) {
+ if (fp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full))
+ return -EWOULDBLOCK;
+ ret = wait_event_interruptible(dev->wait_frame,
+ !list_empty(&dev->sio_full) || !is_present(dev));
+ if (ret)
+ return ret;
+ if (!is_present(dev))
+ return -EIO;
+ }
+ if (count + *f_pos > dev->frame_size)
+ count = dev->frame_size - *f_pos;
+ spin_lock_irqsave(&dev->spinlock, flags);
+ if (list_empty(&dev->sio_full)) {
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ STK_ERROR("BUG: No siobufs ready\n");
+ return 0;
+ }
+ sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ if (copy_to_user(buf, sbuf->buffer + *f_pos, count))
+ return -EFAULT;
+
+ *f_pos += count;
+
+ if (*f_pos >= dev->frame_size) {
+ *f_pos = 0;
+ spin_lock_irqsave(&dev->spinlock, flags);
+ list_move_tail(&sbuf->list, &dev->sio_avail);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ }
+ return count;
+}
+
+static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait)
+{
+ struct stk_camera *dev;
+ struct video_device *vdev;
+
+ vdev = video_devdata(fp);
+
+ if (vdev == NULL)
+ return -EFAULT;
+
+ dev = vdev_to_camera(vdev);
+ if (dev == NULL)
+ return -ENODEV;
+
+ poll_wait(fp, &dev->wait_frame, wait);
+
+ if (!is_present(dev))
+ return POLLERR;
+
+ if (!list_empty(&dev->sio_full))
+ return (POLLIN | POLLRDNORM);
+
+ return 0;
+}
+
+
+static void stk_v4l_vm_open(struct vm_area_struct *vma)
+{
+ struct stk_sio_buffer *sbuf = vma->vm_private_data;
+ sbuf->mapcount++;
+}
+static void stk_v4l_vm_close(struct vm_area_struct *vma)
+{
+ struct stk_sio_buffer *sbuf = vma->vm_private_data;
+ sbuf->mapcount--;
+ if (sbuf->mapcount == 0)
+ sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED;
+}
+static struct vm_operations_struct stk_v4l_vm_ops = {
+ .open = stk_v4l_vm_open,
+ .close = stk_v4l_vm_close
+};
+
+static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma)
+{
+ unsigned int i;
+ int ret;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ struct stk_camera *dev;
+ struct video_device *vdev;
+ struct stk_sio_buffer *sbuf = NULL;
+
+ if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))
+ return -EINVAL;
+
+ vdev = video_devdata(fp);
+ dev = vdev_to_camera(vdev);
+
+ for (i = 0; i < dev->n_sbufs; i++) {
+ if (dev->sio_bufs[i].v4lbuf.m.offset == offset) {
+ sbuf = dev->sio_bufs + i;
+ break;
+ }
+ }
+ if (sbuf == NULL)
+ return -EINVAL;
+ ret = remap_vmalloc_range(vma, sbuf->buffer, 0);
+ if (ret)
+ return ret;
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_private_data = sbuf;
+ vma->vm_ops = &stk_v4l_vm_ops;
+ sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED;
+ stk_v4l_vm_open(vma);
+ return 0;
+}
+
+/* v4l ioctl handlers */
+
+static int stk_vidioc_querycap(struct file *filp,
+ void *priv, struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "stk");
+ strcpy(cap->card, "stk");
+ cap->version = DRIVER_VERSION_NUM;
+
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+ | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int stk_vidioc_enum_input(struct file *filp,
+ void *priv, struct v4l2_input *input)
+{
+ if (input->index != 0)
+ return -EINVAL;
+
+ strcpy(input->name, "Syntek USB Camera");
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ return 0;
+}
+
+
+static int stk_vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int stk_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ else
+ return 0;
+}
+
+/* from vivi.c */
+static int stk_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a)
+{
+ return 0;
+}
+
+/* List of all V4Lv2 controls supported by the driver */
+static struct v4l2_queryctrl stk_controls[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 0xffff,
+ .step = 0x0100,
+ .default_value = 0x6000,
+ },
+ /*TODO: get more controls to work */
+};
+
+static int stk_vidioc_queryctrl(struct file *filp,
+ void *priv, struct v4l2_queryctrl *c)
+{
+ int i;
+ int nbr;
+ nbr = ARRAY_SIZE(stk_controls);
+
+ for (i = 0; i < nbr; i++) {
+ if (stk_controls[i].id == c->id) {
+ memcpy(c, &stk_controls[i],
+ sizeof(struct v4l2_queryctrl));
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int stk_vidioc_g_ctrl(struct file *filp,
+ void *priv, struct v4l2_control *c)
+{
+ struct stk_camera *dev = priv;
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = dev->vsettings.brightness;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int stk_vidioc_s_ctrl(struct file *filp,
+ void *priv, struct v4l2_control *c)
+{
+ struct stk_camera *dev = priv;
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev->vsettings.brightness = c->value;
+ return stk_sensor_set_brightness(dev, c->value >> 8);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static int stk_vidioc_enum_fmt_cap(struct file *filp,
+ void *priv, struct v4l2_fmtdesc *fmtd)
+{
+ fmtd->flags = 0;
+
+ switch (fmtd->index) {
+ case 0:
+ fmtd->pixelformat = V4L2_PIX_FMT_RGB565;
+ strcpy(fmtd->description, "r5g6b5");
+ break;
+ case 1:
+ fmtd->pixelformat = V4L2_PIX_FMT_RGB565X;
+ strcpy(fmtd->description, "r5g6b5BE");
+ break;
+ case 2:
+ fmtd->pixelformat = V4L2_PIX_FMT_UYVY;
+ strcpy(fmtd->description, "yuv4:2:2");
+ break;
+ case 3:
+ fmtd->pixelformat = V4L2_PIX_FMT_SBGGR8;
+ strcpy(fmtd->description, "Raw bayer");
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct stk_size {
+ unsigned w;
+ unsigned h;
+ enum stk_mode m;
+} stk_sizes[] = {
+ { .w = 1280, .h = 1024, .m = MODE_SXGA, },
+ { .w = 640, .h = 480, .m = MODE_VGA, },
+ { .w = 352, .h = 288, .m = MODE_CIF, },
+ { .w = 320, .h = 240, .m = MODE_QVGA, },
+ { .w = 176, .h = 144, .m = MODE_QCIF, },
+};
+
+static int stk_vidioc_g_fmt_cap(struct file *filp,
+ void *priv, struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix_format = &f->fmt.pix;
+ struct stk_camera *dev = priv;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(stk_sizes)
+ && stk_sizes[i].m != dev->vsettings.mode;
+ i++);
+ if (i == ARRAY_SIZE(stk_sizes)) {
+ STK_ERROR("ERROR: mode invalid\n");
+ return -EINVAL;
+ }
+ pix_format->width = stk_sizes[i].w;
+ pix_format->height = stk_sizes[i].h;
+ pix_format->field = V4L2_FIELD_NONE;
+ pix_format->colorspace = V4L2_COLORSPACE_SRGB;
+ pix_format->priv = 0;
+ pix_format->pixelformat = dev->vsettings.palette;
+ if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8)
+ pix_format->bytesperline = pix_format->width;
+ else
+ pix_format->bytesperline = 2 * pix_format->width;
+ pix_format->sizeimage = pix_format->bytesperline
+ * pix_format->height;
+ return 0;
+}
+
+static int stk_vidioc_try_fmt_cap(struct file *filp,
+ void *priv, struct v4l2_format *fmtd)
+{
+ int i;
+ switch (fmtd->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_SBGGR8:
+ break;
+ default:
+ return -EINVAL;
+ }
+ for (i = 1; i < ARRAY_SIZE(stk_sizes); i++) {
+ if (fmtd->fmt.pix.width > stk_sizes[i].w)
+ break;
+ }
+ if (i == ARRAY_SIZE(stk_sizes)
+ || (abs(fmtd->fmt.pix.width - stk_sizes[i-1].w)
+ < abs(fmtd->fmt.pix.width - stk_sizes[i].w))) {
+ fmtd->fmt.pix.height = stk_sizes[i-1].h;
+ fmtd->fmt.pix.width = stk_sizes[i-1].w;
+ fmtd->fmt.pix.priv = i - 1;
+ } else {
+ fmtd->fmt.pix.height = stk_sizes[i].h;
+ fmtd->fmt.pix.width = stk_sizes[i].w;
+ fmtd->fmt.pix.priv = i;
+ }
+
+ fmtd->fmt.pix.field = V4L2_FIELD_NONE;
+ fmtd->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+ if (fmtd->fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8)
+ fmtd->fmt.pix.bytesperline = fmtd->fmt.pix.width;
+ else
+ fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width;
+ fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline
+ * fmtd->fmt.pix.height;
+ return 0;
+}
+
+static int stk_vidioc_s_fmt_cap(struct file *filp,
+ void *priv, struct v4l2_format *fmtd)
+{
+ int ret;
+ struct stk_camera *dev = priv;
+
+ if (dev == NULL)
+ return -ENODEV;
+ if (!is_present(dev))
+ return -ENODEV;
+ if (is_streaming(dev))
+ return -EBUSY;
+ if (dev->owner && dev->owner != filp)
+ return -EBUSY;
+ dev->owner = filp;
+ ret = stk_vidioc_try_fmt_cap(filp, priv, fmtd);
+ if (ret)
+ return ret;
+
+ dev->vsettings.palette = fmtd->fmt.pix.pixelformat;
+ stk_free_buffers(dev);
+ dev->frame_size = fmtd->fmt.pix.sizeimage;
+ dev->vsettings.mode = stk_sizes[fmtd->fmt.pix.priv].m;
+
+ stk_initialise(dev);
+ /* This registers controls some timings, not sure of what. */
+ stk_camera_write_reg(dev, 0x001b, 0x0e);
+ if (dev->vsettings.mode == MODE_SXGA)
+ stk_camera_write_reg(dev, 0x001c, 0x0e);
+ else
+ stk_camera_write_reg(dev, 0x001c, 0x46);
+ /*
+ * Registers 0x0115 0x0114 are the size of each line (bytes),
+ * regs 0x0117 0x0116 are the heigth of the image.
+ */
+ stk_camera_write_reg(dev, 0x0115,
+ (fmtd->fmt.pix.bytesperline >> 8) & 0xff);
+ stk_camera_write_reg(dev, 0x0114,
+ fmtd->fmt.pix.bytesperline & 0xff);
+ stk_camera_write_reg(dev, 0x0117,
+ (fmtd->fmt.pix.height >> 8) & 0xff);
+ stk_camera_write_reg(dev, 0x0116,
+ fmtd->fmt.pix.height & 0xff);
+ return stk_sensor_configure(dev);
+}
+
+static int stk_vidioc_reqbufs(struct file *filp,
+ void *priv, struct v4l2_requestbuffers *rb)
+{
+ struct stk_camera *dev = priv;
+
+ if (dev == NULL)
+ return -ENODEV;
+ if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (rb->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+ if (is_streaming(dev)
+ || (dev->owner && dev->owner != filp))
+ return -EBUSY;
+ dev->owner = filp;
+
+ /*FIXME If they ask for zero, we must stop streaming and free */
+ if (rb->count < 3)
+ rb->count = 3;
+ /* Arbitrary limit */
+ else if (rb->count > 5)
+ rb->count = 5;
+
+ stk_allocate_buffers(dev, rb->count);
+ rb->count = dev->n_sbufs;
+ return 0;
+}
+
+static int stk_vidioc_querybuf(struct file *filp,
+ void *priv, struct v4l2_buffer *buf)
+{
+ int index;
+ struct stk_camera *dev = priv;
+ struct stk_sio_buffer *sbuf;
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ index = buf->index;
+
+ if (index < 0 || index >= dev->n_sbufs)
+ return -EINVAL;
+ sbuf = dev->sio_bufs + buf->index;
+ *buf = sbuf->v4lbuf;
+ return 0;
+}
+
+static int stk_vidioc_qbuf(struct file *filp,
+ void *priv, struct v4l2_buffer *buf)
+{
+ struct stk_camera *dev = priv;
+ struct stk_sio_buffer *sbuf;
+ unsigned long flags;
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (buf->index < 0 || buf->index >= dev->n_sbufs)
+ return -EINVAL;
+ sbuf = dev->sio_bufs + buf->index;
+ if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED)
+ return 0;
+ sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED;
+ sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE;
+ spin_lock_irqsave(&dev->spinlock, flags);
+ list_add_tail(&sbuf->list, &dev->sio_avail);
+ *buf = sbuf->v4lbuf;
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ return 0;
+}
+
+static int stk_vidioc_dqbuf(struct file *filp,
+ void *priv, struct v4l2_buffer *buf)
+{
+ struct stk_camera *dev = priv;
+ struct stk_sio_buffer *sbuf;
+ unsigned long flags;
+ int ret;
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
+ || !is_streaming(dev))
+ return -EINVAL;
+
+ if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full))
+ return -EWOULDBLOCK;
+ ret = wait_event_interruptible(dev->wait_frame,
+ !list_empty(&dev->sio_full) || !is_present(dev));
+ if (ret)
+ return ret;
+ if (!is_present(dev))
+ return -EIO;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list);
+ list_del_init(&sbuf->list);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
+ sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE;
+ sbuf->v4lbuf.sequence = ++dev->sequence;
+ do_gettimeofday(&sbuf->v4lbuf.timestamp);
+
+ *buf = sbuf->v4lbuf;
+ return 0;
+}
+
+static int stk_vidioc_streamon(struct file *filp,
+ void *priv, enum v4l2_buf_type type)
+{
+ struct stk_camera *dev = priv;
+ if (is_streaming(dev))
+ return 0;
+ if (dev->sio_bufs == NULL)
+ return -EINVAL;
+ dev->sequence = 0;
+ return stk_start_stream(dev);
+}
+
+static int stk_vidioc_streamoff(struct file *filp,
+ void *priv, enum v4l2_buf_type type)
+{
+ struct stk_camera *dev = priv;
+ unsigned long flags;
+ int i;
+ stk_stop_stream(dev);
+ spin_lock_irqsave(&dev->spinlock, flags);
+ INIT_LIST_HEAD(&dev->sio_avail);
+ INIT_LIST_HEAD(&dev->sio_full);
+ for (i = 0; i < dev->n_sbufs; i++) {
+ INIT_LIST_HEAD(&dev->sio_bufs[i].list);
+ dev->sio_bufs[i].v4lbuf.flags = 0;
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ return 0;
+}
+
+
+static int stk_vidioc_g_parm(struct file *filp,
+ void *priv, struct v4l2_streamparm *sp)
+{
+ if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp->parm.capture.capability = 0;
+ sp->parm.capture.capturemode = 0;
+ /*FIXME This is not correct */
+ sp->parm.capture.timeperframe.numerator = 1;
+ sp->parm.capture.timeperframe.denominator = 30;
+ sp->parm.capture.readbuffers = 2;
+ sp->parm.capture.extendedmode = 0;
+ return 0;
+}
+
+static struct file_operations v4l_stk_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l_stk_open,
+ .release = v4l_stk_release,
+ .read = v4l_stk_read,
+ .poll = v4l_stk_poll,
+ .mmap = v4l_stk_mmap,
+ .ioctl = video_ioctl2,
+ .llseek = no_llseek
+};
+
+static void stk_v4l_dev_release(struct video_device *vd)
+{
+}
+
+static struct video_device stk_v4l_data = {
+ .name = "stkwebcam",
+ .type = VFL_TYPE_GRABBER,
+ .type2 = VID_TYPE_CAPTURE,
+ .minor = -1,
+ .tvnorms = V4L2_STD_UNKNOWN,
+ .current_norm = V4L2_STD_UNKNOWN,
+ .fops = &v4l_stk_fops,
+ .release = stk_v4l_dev_release,
+
+ .vidioc_querycap = stk_vidioc_querycap,
+ .vidioc_enum_fmt_cap = stk_vidioc_enum_fmt_cap,
+ .vidioc_try_fmt_cap = stk_vidioc_try_fmt_cap,
+ .vidioc_s_fmt_cap = stk_vidioc_s_fmt_cap,
+ .vidioc_g_fmt_cap = stk_vidioc_g_fmt_cap,
+ .vidioc_enum_input = stk_vidioc_enum_input,
+ .vidioc_s_input = stk_vidioc_s_input,
+ .vidioc_g_input = stk_vidioc_g_input,
+ .vidioc_s_std = stk_vidioc_s_std,
+ .vidioc_reqbufs = stk_vidioc_reqbufs,
+ .vidioc_querybuf = stk_vidioc_querybuf,
+ .vidioc_qbuf = stk_vidioc_qbuf,
+ .vidioc_dqbuf = stk_vidioc_dqbuf,
+ .vidioc_streamon = stk_vidioc_streamon,
+ .vidioc_streamoff = stk_vidioc_streamoff,
+ .vidioc_queryctrl = stk_vidioc_queryctrl,
+ .vidioc_g_ctrl = stk_vidioc_g_ctrl,
+ .vidioc_s_ctrl = stk_vidioc_s_ctrl,
+ .vidioc_g_parm = stk_vidioc_g_parm,
+};
+
+
+static int stk_register_video_device(struct stk_camera *dev)
+{
+ int err;
+
+ dev->vdev = stk_v4l_data;
+ dev->vdev.debug = debug;
+ dev->vdev.dev = &dev->interface->dev;
+ dev->vdev.priv = dev;
+ err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
+ if (err)
+ STK_ERROR("v4l registration failed\n");
+ else
+ STK_INFO("Syntek USB2.0 Camera is now controlling video device"
+ " /dev/video%d\n", dev->vdev.minor);
+ return err;
+}
+
+
+/* USB Stuff */
+
+static int stk_camera_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ int i;
+ int err;
+
+ struct stk_camera *dev = NULL;
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+
+ dev = kzalloc(sizeof(struct stk_camera), GFP_KERNEL);
+ if (dev == NULL) {
+ STK_ERROR("Out of memory !\n");
+ return -ENOMEM;
+ }
+
+ kref_init(&dev->kref);
+ spin_lock_init(&dev->spinlock);
+ init_waitqueue_head(&dev->wait_frame);
+
+ dev->udev = udev;
+ dev->interface = interface;
+ usb_get_intf(interface);
+
+ dev->vsettings.vflip = vflip;
+ dev->vsettings.hflip = hflip;
+ dev->n_sbufs = 0;
+ set_present(dev);
+
+ /* Set up the endpoint information
+ * use only the first isoc-in endpoint
+ * for the current alternate setting */
+ iface_desc = interface->cur_altsetting;
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (!dev->isoc_ep
+ && ((endpoint->bEndpointAddress
+ & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+ && ((endpoint->bmAttributes
+ & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC)) {
+ /* we found an isoc in endpoint */
+ dev->isoc_ep = (endpoint->bEndpointAddress & 0xF);
+ break;
+ }
+ }
+ if (!dev->isoc_ep) {
+ STK_ERROR("Could not find isoc-in endpoint");
+ kref_put(&dev->kref, stk_camera_cleanup);
+ return -ENODEV;
+ }
+ dev->vsettings.brightness = 0x7fff;
+ dev->vsettings.palette = V4L2_PIX_FMT_RGB565;
+ dev->vsettings.mode = MODE_VGA;
+ dev->frame_size = 640*480*2;
+
+ INIT_LIST_HEAD(&dev->sio_avail);
+ INIT_LIST_HEAD(&dev->sio_full);
+
+ usb_set_intfdata(interface, dev);
+
+ err = stk_register_video_device(dev);
+ if (err) {
+ kref_put(&dev->kref, stk_camera_cleanup);
+ return err;
+ }
+
+ stk_create_sysfs_files(&dev->vdev);
+
+ return 0;
+}
+
+static void stk_camera_disconnect(struct usb_interface *interface)
+{
+ struct stk_camera *dev = usb_get_intfdata(interface);
+
+ usb_set_intfdata(interface, NULL);
+ unset_present(dev);
+
+ wake_up_interruptible(&dev->wait_frame);
+ stk_remove_sysfs_files(&dev->vdev);
+
+ kref_put(&dev->kref, stk_camera_cleanup);
+}
+
+static struct usb_driver stk_camera_driver = {
+ .name = "stkwebcam",
+ .probe = stk_camera_probe,
+ .disconnect = stk_camera_disconnect,
+ .id_table = stkwebcam_table,
+};
+
+
+static int __init stk_camera_init(void)
+{
+ int result;
+
+ result = usb_register(&stk_camera_driver);
+ if (result)
+ STK_ERROR("usb_register failed ! Error number %d\n", result);
+
+
+ return result;
+}
+
+static void __exit stk_camera_exit(void)
+{
+ usb_deregister(&stk_camera_driver);
+}
+
+module_init(stk_camera_init);
+module_exit(stk_camera_exit);
+
+
diff --git a/drivers/media/video/stk-webcam.h b/drivers/media/video/stk-webcam.h
new file mode 100644
index 000000000000..7e989d1ac1e0
--- /dev/null
+++ b/drivers/media/video/stk-webcam.h
@@ -0,0 +1,138 @@
+/*
+ * stk-webcam.h : Driver for Syntek 1125 USB webcam controller
+ *
+ * Copyright (C) 2006 Nicolas VIVIEN
+ * Copyright 2007-2008 Jaime Velasco Juan <jsagarribay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef STKWEBCAM_H
+#define STKWEBCAM_H
+
+#include <linux/usb.h>
+#include <media/v4l2-common.h>
+
+#define DRIVER_VERSION "v0.0.1"
+#define DRIVER_VERSION_NUM 0x000001
+
+#define MAX_ISO_BUFS 3
+#define ISO_FRAMES_PER_DESC 16
+#define ISO_MAX_FRAME_SIZE 3 * 1024
+#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE)
+
+
+#define PREFIX "stkwebcam: "
+#define STK_INFO(str, args...) printk(KERN_INFO PREFIX str, ##args)
+#define STK_ERROR(str, args...) printk(KERN_ERR PREFIX str, ##args)
+#define STK_WARNING(str, args...) printk(KERN_WARNING PREFIX str, ##args)
+
+struct stk_iso_buf {
+ void *data;
+ int length;
+ int read;
+ struct urb *urb;
+};
+
+/* Streaming IO buffers */
+struct stk_sio_buffer {
+ struct v4l2_buffer v4lbuf;
+ char *buffer;
+ int mapcount;
+ struct stk_camera *dev;
+ struct list_head list;
+};
+
+enum stk_mode {MODE_VGA, MODE_SXGA, MODE_CIF, MODE_QVGA, MODE_QCIF};
+
+struct stk_video {
+ enum stk_mode mode;
+ int brightness;
+ __u32 palette;
+ int hflip;
+ int vflip;
+};
+
+enum stk_status {
+ S_PRESENT = 1,
+ S_INITIALISED = 2,
+ S_MEMALLOCD = 4,
+ S_STREAMING = 8,
+};
+#define is_present(dev) ((dev)->status & S_PRESENT)
+#define is_initialised(dev) ((dev)->status & S_INITIALISED)
+#define is_streaming(dev) ((dev)->status & S_STREAMING)
+#define is_memallocd(dev) ((dev)->status & S_MEMALLOCD)
+#define set_present(dev) ((dev)->status = S_PRESENT)
+#define unset_present(dev) ((dev)->status &= \
+ ~(S_PRESENT|S_INITIALISED|S_STREAMING))
+#define set_initialised(dev) ((dev)->status |= S_INITIALISED)
+#define set_memallocd(dev) ((dev)->status |= S_MEMALLOCD)
+#define unset_memallocd(dev) ((dev)->status &= ~S_MEMALLOCD)
+#define set_streaming(dev) ((dev)->status |= S_STREAMING)
+#define unset_streaming(dev) ((dev)->status &= ~S_STREAMING)
+
+struct regval {
+ unsigned reg;
+ unsigned val;
+};
+
+struct stk_camera {
+ struct video_device vdev;
+ struct usb_device *udev;
+ struct usb_interface *interface;
+ int webcam_model;
+ struct file *owner;
+
+ u8 isoc_ep;
+
+ struct kref kref;
+ /* Not sure if this is right */
+ atomic_t urbs_used;
+
+ struct stk_video vsettings;
+
+ enum stk_status status;
+
+ spinlock_t spinlock;
+ wait_queue_head_t wait_frame;
+
+ struct stk_iso_buf *isobufs;
+
+ int frame_size;
+ /* Streaming buffers */
+ unsigned int n_sbufs;
+ struct stk_sio_buffer *sio_bufs;
+ struct list_head sio_avail;
+ struct list_head sio_full;
+ unsigned sequence;
+};
+
+#define to_stk_camera(d) container_of(d, struct stk_camera, kref)
+#define vdev_to_camera(d) container_of(d, struct stk_camera, vdev)
+
+void stk_camera_delete(struct kref *);
+int stk_camera_write_reg(struct stk_camera *, u16, u8);
+int stk_camera_read_reg(struct stk_camera *, u16, int *);
+
+int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val);
+int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val);
+int stk_sensor_init(struct stk_camera *);
+int stk_sensor_configure(struct stk_camera *);
+int stk_sensor_sleep(struct stk_camera *dev);
+int stk_sensor_wakeup(struct stk_camera *dev);
+int stk_sensor_set_brightness(struct stk_camera *dev, int br);
+
+#endif
diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c
index 43225802a551..b4d10f7a4e57 100644
--- a/drivers/media/video/tda7432.c
+++ b/drivers/media/video/tda7432.c
@@ -8,6 +8,7 @@
* Muting and tone control by Jonathan Isom <jisom@ematic.com>
*
* Copyright (c) 2000 Eric Sandeen <eric_sandeen@bigfoot.com>
+ * Copyright (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
* This code is placed under the terms of the GNU General Public License
* Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu)
* Which was based on tda8425.c by Greg Alexander (c) 1998
@@ -276,7 +277,7 @@ static void do_tda7432_init(struct i2c_client *client)
t->volume = 0x3b ; /* -27dB Volume */
if (loudness) /* Turn loudness on? */
t->volume |= TDA7432_LD_ON;
- t->muted = VIDEO_AUDIO_MUTE;
+ t->muted = 1;
t->treble = TDA7432_TREBLE_0DB; /* 0dB Treble */
t->bass = TDA7432_BASS_0DB; /* 0dB Bass */
t->lf = TDA7432_ATTEN_0DB; /* 0dB attenuation */
@@ -332,151 +333,160 @@ static int tda7432_detach(struct i2c_client *client)
return 0;
}
-static int tda7432_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tda7432_get_ctrl(struct i2c_client *client,
+ struct v4l2_control *ctrl)
{
struct tda7432 *t = i2c_get_clientdata(client);
- v4l_dbg(2, debug,client,"In tda7432_command\n");
- if (debug>1)
- v4l_i2c_print_ioctl(client,cmd);
- switch (cmd) {
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
-
- /* Query card - scale from TDA7432 settings to V4L settings */
- case VIDIOCGAUDIO:
- {
- struct video_audio *va = arg;
-
- va->flags |= VIDEO_AUDIO_VOLUME |
- VIDEO_AUDIO_BASS |
- VIDEO_AUDIO_TREBLE |
- VIDEO_AUDIO_MUTABLE;
- if (t->muted)
- va->flags |= VIDEO_AUDIO_MUTE;
- va->mode |= VIDEO_SOUND_STEREO;
- /* Master volume control
- * V4L volume is min 0, max 65535
- * TDA7432 Volume:
- * Min (-79dB) is 0x6f
- * Max (+20dB) is 0x07 (630)
- * Max (0dB) is 0x20 (829)
- * (Mask out bit 7 of vol - it's for the loudness setting)
- */
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=t->muted;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
if (!maxvol){ /* max +20db */
- va->volume = ( 0x6f - (t->volume & 0x7F) ) * 630;
+ ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 630;
} else { /* max 0db */
- va->volume = ( 0x6f - (t->volume & 0x7F) ) * 829;
+ ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 829;
}
-
- /* Balance depends on L,R attenuation
- * V4L balance is 0 to 65535, middle is 32768
- * TDA7432 attenuation: min (0dB) is 0, max (-37.5dB) is 0x1f
- * to scale up to V4L numbers, mult by 1057
- * attenuation exists for lf, lr, rf, rr
- * we use only lf and rf (front channels)
- */
-
+ return 0;
+ case V4L2_CID_AUDIO_BALANCE:
+ {
if ( (t->lf) < (t->rf) )
/* right is attenuated, balance shifted left */
- va->balance = (32768 - 1057*(t->rf));
+ ctrl->value = (32768 - 1057*(t->rf));
else
/* left is attenuated, balance shifted right */
- va->balance = (32768 + 1057*(t->lf));
-
+ ctrl->value = (32768 + 1057*(t->lf));
+ return 0;
+ }
+ case V4L2_CID_AUDIO_BASS:
+ {
/* Bass/treble 4 bits each */
- va->bass=t->bass;
- if(va->bass >= 0x8)
- va->bass = ~(va->bass - 0x8) & 0xf;
- va->bass = (va->bass << 12)+(va->bass << 8)+(va->bass << 4)+(va->bass);
- va->treble=t->treble;
- if(va->treble >= 0x8)
- va->treble = ~(va->treble - 0x8) & 0xf;
- va->treble = (va->treble << 12)+(va->treble << 8)+(va->treble << 4)+(va->treble);
-
- break; /* VIDIOCGAUDIO case */
+ int bass=t->bass;
+ if(bass >= 0x8)
+ bass = ~(bass - 0x8) & 0xf;
+ ctrl->value = (bass << 12)+(bass << 8)+(bass << 4)+(bass);
+ return 0;
}
-
- /* Set card - scale from V4L settings to TDA7432 settings */
- case VIDIOCSAUDIO:
+ case V4L2_CID_AUDIO_TREBLE:
{
- struct video_audio *va = arg;
+ int treble=t->treble;
+ if(treble >= 0x8)
+ treble = ~(treble - 0x8) & 0xf;
+ ctrl->value = (treble << 12)+(treble << 8)+(treble << 4)+(treble);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
- if(va->flags & VIDEO_AUDIO_VOLUME){
- if(!maxvol){ /* max +20db */
- t->volume = 0x6f - ((va->volume)/630);
- } else { /* max 0db */
- t->volume = 0x6f - ((va->volume)/829);
- }
+static int tda7432_set_ctrl(struct i2c_client *client,
+ struct v4l2_control *ctrl)
+{
+ struct tda7432 *t = i2c_get_clientdata(client);
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ t->muted=ctrl->value;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ if(!maxvol){ /* max +20db */
+ t->volume = 0x6f - ((ctrl->value)/630);
+ } else { /* max 0db */
+ t->volume = 0x6f - ((ctrl->value)/829);
+ }
if (loudness) /* Turn on the loudness bit */
t->volume |= TDA7432_LD_ON;
- tda7432_write(client,TDA7432_VL, t->volume);
- }
-
- if(va->flags & VIDEO_AUDIO_BASS)
- {
- t->bass = va->bass >> 12;
- if(t->bass>= 0x8)
- t->bass = (~t->bass & 0xf) + 0x8 ;
- }
- if(va->flags & VIDEO_AUDIO_TREBLE)
- {
- t->treble= va->treble >> 12;
- if(t->treble>= 0x8)
- t->treble = (~t->treble & 0xf) + 0x8 ;
- }
- if(va->flags & (VIDEO_AUDIO_TREBLE| VIDEO_AUDIO_BASS))
- tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble );
-
- if(va->flags & VIDEO_AUDIO_BALANCE) {
- if (va->balance < 32768)
- {
+ tda7432_write(client,TDA7432_VL, t->volume);
+ return 0;
+ case V4L2_CID_AUDIO_BALANCE:
+ if (ctrl->value < 32768) {
/* shifted to left, attenuate right */
- t->rr = (32768 - va->balance)/1057;
+ t->rr = (32768 - ctrl->value)/1057;
t->rf = t->rr;
t->lr = TDA7432_ATTEN_0DB;
t->lf = TDA7432_ATTEN_0DB;
- }
- else if(va->balance > 32769)
- {
+ } else if(ctrl->value > 32769) {
/* shifted to right, attenuate left */
- t->lf = (va->balance - 32768)/1057;
+ t->lf = (ctrl->value - 32768)/1057;
t->lr = t->lf;
t->rr = TDA7432_ATTEN_0DB;
t->rf = TDA7432_ATTEN_0DB;
- }
- else
- {
+ } else {
/* centered */
t->rr = TDA7432_ATTEN_0DB;
t->rf = TDA7432_ATTEN_0DB;
t->lf = TDA7432_ATTEN_0DB;
t->lr = TDA7432_ATTEN_0DB;
}
- }
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ t->bass = ctrl->value >> 12;
+ if(t->bass>= 0x8)
+ t->bass = (~t->bass & 0xf) + 0x8 ;
+
+ tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble );
+ return 0;
+ case V4L2_CID_AUDIO_TREBLE:
+ t->treble= ctrl->value >> 12;
+ if(t->treble>= 0x8)
+ t->treble = (~t->treble & 0xf) + 0x8 ;
+
+ tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble );
+ return 0;
+ default:
+ return -EINVAL;
+ }
- t->muted=(va->flags & VIDEO_AUDIO_MUTE);
- if (t->muted)
- {
- /* Mute & update balance*/
- tda7432_write(client,TDA7432_LF, t->lf | TDA7432_MUTE);
- tda7432_write(client,TDA7432_LR, t->lr | TDA7432_MUTE);
- tda7432_write(client,TDA7432_RF, t->rf | TDA7432_MUTE);
- tda7432_write(client,TDA7432_RR, t->rr | TDA7432_MUTE);
- } else {
- tda7432_write(client,TDA7432_LF, t->lf);
- tda7432_write(client,TDA7432_LR, t->lr);
- tda7432_write(client,TDA7432_RF, t->rf);
- tda7432_write(client,TDA7432_RR, t->rr);
- }
+ /* Used for both mute and balance changes */
+ if (t->muted)
+ {
+ /* Mute & update balance*/
+ tda7432_write(client,TDA7432_LF, t->lf | TDA7432_MUTE);
+ tda7432_write(client,TDA7432_LR, t->lr | TDA7432_MUTE);
+ tda7432_write(client,TDA7432_RF, t->rf | TDA7432_MUTE);
+ tda7432_write(client,TDA7432_RR, t->rr | TDA7432_MUTE);
+ } else {
+ tda7432_write(client,TDA7432_LF, t->lf);
+ tda7432_write(client,TDA7432_LR, t->lr);
+ tda7432_write(client,TDA7432_RF, t->rf);
+ tda7432_write(client,TDA7432_RR, t->rr);
+ }
+ return 0;
+}
- break;
+static int tda7432_command(struct i2c_client *client,
+ unsigned int cmd, void *arg)
+{
+ v4l_dbg(2, debug,client,"In tda7432_command\n");
+ if (debug>1)
+ v4l_i2c_print_ioctl(client,cmd);
+
+ switch (cmd) {
+ /* --- v4l ioctls --- */
+ /* take care: bttv does userspace copying, we'll get a
+ kernel pointer here... */
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ default:
+ return -EINVAL;
+ }
+ return v4l2_ctrl_query_fill_std(qc);
+ }
+ case VIDIOC_S_CTRL:
+ return tda7432_set_ctrl(client, arg);
- } /* end of VIDEOCSAUDIO case */
+ case VIDIOC_G_CTRL:
+ return tda7432_get_ctrl(client, arg);
} /* end of (cmd) switch */
diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c
index 0e5cf459d3ed..55bc89a6f069 100644
--- a/drivers/media/video/tda8290.c
+++ b/drivers/media/video/tda8290.c
@@ -25,12 +25,14 @@
#include <linux/videodev.h>
#include "tuner-i2c.h"
#include "tda8290.h"
+#include "tda827x.h"
+#include "tda18271.h"
-static int debug = 0;
+static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");
-#define PREFIX "tda8290 "
+#define PREFIX "tda8290"
/* ---------------------------------------------------------------------- */
@@ -38,345 +40,71 @@ struct tda8290_priv {
struct tuner_i2c_props i2c_props;
unsigned char tda8290_easy_mode;
- unsigned char tda827x_lpsel;
- unsigned char tda827x_addr;
- unsigned char tda827x_ver;
- unsigned int sgIF;
-
- u32 frequency;
-
- unsigned int *lna_cfg;
- int (*tuner_callback) (void *dev, int command,int arg);
-};
-
-/* ---------------------------------------------------------------------- */
-
-struct tda827x_data {
- u32 lomax;
- u8 spd;
- u8 bs;
- u8 bp;
- u8 cp;
- u8 gc3;
- u8 div1p5;
-};
-
- /* Note lomax entry is lo / 62500 */
-
-static struct tda827x_data tda827x_analog[] = {
- { .lomax = 992, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, /* 62 MHz */
- { .lomax = 1056, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, /* 66 MHz */
- { .lomax = 1216, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, /* 76 MHz */
- { .lomax = 1344, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, /* 84 MHz */
- { .lomax = 1488, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, /* 93 MHz */
- { .lomax = 1568, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, /* 98 MHz */
- { .lomax = 1744, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, /* 109 MHz */
- { .lomax = 1968, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, /* 123 MHz */
- { .lomax = 2128, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, /* 133 MHz */
- { .lomax = 2416, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, /* 151 MHz */
- { .lomax = 2464, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, /* 154 MHz */
- { .lomax = 2896, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0}, /* 181 MHz */
- { .lomax = 2960, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, /* 185 MHz */
- { .lomax = 3472, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, /* 217 MHz */
- { .lomax = 3904, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, /* 244 MHz */
- { .lomax = 4240, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, /* 265 MHz */
- { .lomax = 4832, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, /* 302 MHz */
- { .lomax = 5184, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, /* 324 MHz */
- { .lomax = 5920, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, /* 370 MHz */
- { .lomax = 7264, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, /* 454 MHz */
- { .lomax = 7888, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, /* 493 MHz */
- { .lomax = 8480, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, /* 530 MHz */
- { .lomax = 8864, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, /* 554 MHz */
- { .lomax = 9664, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, /* 604 MHz */
- { .lomax = 11088, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, /* 696 MHz */
- { .lomax = 11840, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, /* 740 MHz */
- { .lomax = 13120, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, /* 820 MHz */
- { .lomax = 13840, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, /* 865 MHz */
- { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0} /* End */
-};
-
-static void tda827x_set_analog_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- unsigned char tuner_reg[8];
- unsigned char reg2[2];
- u32 N;
- int i;
- struct tda8290_priv *priv = fe->tuner_priv;
- struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags = 0};
- unsigned int freq = params->frequency;
-
- if (params->mode == V4L2_TUNER_RADIO)
- freq = freq / 1000;
-
- N = freq + priv->sgIF;
- i = 0;
- while (tda827x_analog[i].lomax < N) {
- if(tda827x_analog[i + 1].lomax == 0)
- break;
- i++;
- }
-
- N = N << tda827x_analog[i].spd;
-
- tuner_reg[0] = 0;
- tuner_reg[1] = (unsigned char)(N>>8);
- tuner_reg[2] = (unsigned char) N;
- tuner_reg[3] = 0x40;
- tuner_reg[4] = 0x52 + (priv->tda827x_lpsel << 5);
- tuner_reg[5] = (tda827x_analog[i].spd << 6) + (tda827x_analog[i].div1p5 <<5) +
- (tda827x_analog[i].bs <<3) + tda827x_analog[i].bp;
- tuner_reg[6] = 0x8f + (tda827x_analog[i].gc3 << 4);
- tuner_reg[7] = 0x8f;
-
- msg.buf = tuner_reg;
- msg.len = 8;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- msg.buf= reg2;
- msg.len = 2;
- reg2[0] = 0x80;
- reg2[1] = 0;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- reg2[0] = 0x60;
- reg2[1] = 0xbf;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- reg2[0] = 0x30;
- reg2[1] = tuner_reg[4] + 0x80;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- msleep(1);
- reg2[0] = 0x30;
- reg2[1] = tuner_reg[4] + 4;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- msleep(1);
- reg2[0] = 0x30;
- reg2[1] = tuner_reg[4];
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- msleep(550);
- reg2[0] = 0x30;
- reg2[1] = (tuner_reg[4] & 0xfc) + tda827x_analog[i].cp ;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- reg2[0] = 0x60;
- reg2[1] = 0x3f;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- reg2[0] = 0x80;
- reg2[1] = 0x08; // Vsync en
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-}
-static void tda827x_agcf(struct dvb_frontend *fe)
-{
- struct tda8290_priv *priv = fe->tuner_priv;
- unsigned char data[] = {0x80, 0x0c};
- struct i2c_msg msg = {.addr = priv->tda827x_addr, .buf = data,
- .flags = 0, .len = 2};
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-}
-
-/* ---------------------------------------------------------------------- */
+ unsigned char tda827x_addr;
-struct tda827xa_data {
- u32 lomax;
- u8 svco;
- u8 spd;
- u8 scr;
- u8 sbs;
- u8 gc3;
-};
+ unsigned char ver;
+#define TDA8290 1
+#define TDA8295 2
+#define TDA8275 4
+#define TDA8275A 8
+#define TDA18271 16
-static struct tda827xa_data tda827xa_analog[] = {
- { .lomax = 910, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 3}, /* 56.875 MHz */
- { .lomax = 1076, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3}, /* 67.25 MHz */
- { .lomax = 1300, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3}, /* 81.25 MHz */
- { .lomax = 1560, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3}, /* 97.5 MHz */
- { .lomax = 1820, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1}, /* 113.75 MHz */
- { .lomax = 2152, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, /* 134.5 MHz */
- { .lomax = 2464, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, /* 154 MHz */
- { .lomax = 2600, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, /* 162.5 MHz */
- { .lomax = 2928, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, /* 183 MHz */
- { .lomax = 3120, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, /* 195 MHz */
- { .lomax = 3640, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 3}, /* 227.5 MHz */
- { .lomax = 4304, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 3}, /* 269 MHz */
- { .lomax = 5200, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, /* 325 MHz */
- { .lomax = 6240, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3}, /* 390 MHz */
- { .lomax = 7280, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3}, /* 455 MHz */
- { .lomax = 8320, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, /* 520 MHz */
- { .lomax = 8608, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, /* 538 MHz */
- { .lomax = 8864, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, /* 554 MHz */
- { .lomax = 9920, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, /* 620 MHz */
- { .lomax = 10400, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, /* 650 MHz */
- { .lomax = 11200, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, /* 700 MHz */
- { .lomax = 12480, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, /* 780 MHz */
- { .lomax = 13120, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, /* 820 MHz */
- { .lomax = 13920, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, /* 870 MHz */
- { .lomax = 14576, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0}, /* 911 MHz */
- { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} /* End */
+ struct tda827x_config cfg;
};
-static void tda827xa_lna_gain(struct dvb_frontend *fe, int high,
- struct analog_parameters *params)
-{
- struct tda8290_priv *priv = fe->tuner_priv;
- unsigned char buf[] = {0x22, 0x01};
- int arg;
- struct i2c_msg msg = {.addr = priv->i2c_props.addr, .flags = 0, .buf = buf, .len = sizeof(buf)};
-
- if ((priv->lna_cfg == NULL) || (priv->tuner_callback == NULL))
- return;
-
- if (*priv->lna_cfg) {
- if (high)
- tuner_dbg("setting LNA to high gain\n");
- else
- tuner_dbg("setting LNA to low gain\n");
- }
- switch (*priv->lna_cfg) {
- case 0: /* no LNA */
- break;
- case 1: /* switch is GPIO 0 of tda8290 */
- case 2:
- /* turn Vsync on */
- if (params->std & V4L2_STD_MN)
- arg = 1;
- else
- arg = 0;
- if (priv->tuner_callback)
- priv->tuner_callback(priv->i2c_props.adap->algo_data, 1, arg);
- buf[1] = high ? 0 : 1;
- if (*priv->lna_cfg == 2)
- buf[1] = high ? 1 : 0;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
- break;
- case 3: /* switch with GPIO of saa713x */
- if (priv->tuner_callback)
- priv->tuner_callback(priv->i2c_props.adap->algo_data, 0, high);
- break;
- }
-}
+/*---------------------------------------------------------------------*/
-static void tda827xa_set_analog_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
+static int tda8290_i2c_bridge(struct dvb_frontend *fe, int close)
{
- unsigned char tuner_reg[11];
- u32 N;
- int i;
- struct tda8290_priv *priv = fe->tuner_priv;
- struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags = 0, .buf = tuner_reg};
- unsigned int freq = params->frequency;
+ struct tda8290_priv *priv = fe->analog_demod_priv;
- tda827xa_lna_gain(fe, 1, params);
- msleep(10);
-
- if (params->mode == V4L2_TUNER_RADIO)
- freq = freq / 1000;
+ unsigned char enable[2] = { 0x21, 0xC0 };
+ unsigned char disable[2] = { 0x21, 0x00 };
+ unsigned char *msg;
- N = freq + priv->sgIF;
- i = 0;
- while (tda827xa_analog[i].lomax < N) {
- if(tda827xa_analog[i + 1].lomax == 0)
- break;
- i++;
+ if (close) {
+ msg = enable;
+ tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
+ /* let the bridge stabilize */
+ msleep(20);
+ } else {
+ msg = disable;
+ tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
}
- N = N << tda827xa_analog[i].spd;
-
- tuner_reg[0] = 0;
- tuner_reg[1] = (unsigned char)(N>>8);
- tuner_reg[2] = (unsigned char) N;
- tuner_reg[3] = 0;
- tuner_reg[4] = 0x16;
- tuner_reg[5] = (tda827xa_analog[i].spd << 5) + (tda827xa_analog[i].svco << 3) +
- tda827xa_analog[i].sbs;
- tuner_reg[6] = 0x8b + (tda827xa_analog[i].gc3 << 4);
- tuner_reg[7] = 0x1c;
- tuner_reg[8] = 4;
- tuner_reg[9] = 0x20;
- tuner_reg[10] = 0x00;
- msg.len = 11;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- tuner_reg[0] = 0x90;
- tuner_reg[1] = 0xff;
- tuner_reg[2] = 0xe0;
- tuner_reg[3] = 0;
- tuner_reg[4] = 0x99 + (priv->tda827x_lpsel << 1);
- msg.len = 5;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- tuner_reg[0] = 0xa0;
- tuner_reg[1] = 0xc0;
- msg.len = 2;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- tuner_reg[0] = 0x30;
- tuner_reg[1] = 0x10 + tda827xa_analog[i].scr;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- msg.flags = I2C_M_RD;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
- msg.flags = 0;
- tuner_reg[1] >>= 4;
- tuner_dbg("AGC2 gain is: %d\n", tuner_reg[1]);
- if (tuner_reg[1] < 1)
- tda827xa_lna_gain(fe, 0, params);
-
- msleep(100);
- tuner_reg[0] = 0x60;
- tuner_reg[1] = 0x3c;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- msleep(163);
- tuner_reg[0] = 0x50;
- tuner_reg[1] = 0x8f + (tda827xa_analog[i].gc3 << 4);
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- tuner_reg[0] = 0x80;
- tuner_reg[1] = 0x28;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- tuner_reg[0] = 0xb0;
- tuner_reg[1] = 0x01;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- tuner_reg[0] = 0xc0;
- tuner_reg[1] = 0x19 + (priv->tda827x_lpsel << 1);
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
-}
-
-static void tda827xa_agcf(struct dvb_frontend *fe)
-{
- struct tda8290_priv *priv = fe->tuner_priv;
- unsigned char data[] = {0x80, 0x2c};
- struct i2c_msg msg = {.addr = priv->tda827x_addr, .buf = data,
- .flags = 0, .len = 2};
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
+ return 0;
}
-/*---------------------------------------------------------------------*/
-
-static void tda8290_i2c_bridge(struct dvb_frontend *fe, int close)
+static int tda8295_i2c_bridge(struct dvb_frontend *fe, int close)
{
- struct tda8290_priv *priv = fe->tuner_priv;
+ struct tda8290_priv *priv = fe->analog_demod_priv;
- unsigned char enable[2] = { 0x21, 0xC0 };
- unsigned char disable[2] = { 0x21, 0x00 };
+ unsigned char enable[2] = { 0x45, 0xc1 };
+ unsigned char disable[2] = { 0x46, 0x00 };
+ unsigned char buf[3] = { 0x45, 0x01, 0x00 };
unsigned char *msg;
- if(close) {
+
+ if (close) {
msg = enable;
tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
/* let the bridge stabilize */
msleep(20);
} else {
msg = disable;
+ tuner_i2c_xfer_send(&priv->i2c_props, msg, 1);
+ tuner_i2c_xfer_recv(&priv->i2c_props, &msg[1], 1);
+
+ buf[2] = msg[1];
+ buf[2] &= ~0x04;
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 3);
+ msleep(5);
+
+ msg[1] |= 0x04;
tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
}
+
+ return 0;
}
/*---------------------------------------------------------------------*/
@@ -384,55 +112,43 @@ static void tda8290_i2c_bridge(struct dvb_frontend *fe, int close)
static void set_audio(struct dvb_frontend *fe,
struct analog_parameters *params)
{
- struct tda8290_priv *priv = fe->tuner_priv;
+ struct tda8290_priv *priv = fe->analog_demod_priv;
char* mode;
- priv->tda827x_lpsel = 0;
if (params->std & V4L2_STD_MN) {
- priv->sgIF = 92;
priv->tda8290_easy_mode = 0x01;
- priv->tda827x_lpsel = 1;
mode = "MN";
} else if (params->std & V4L2_STD_B) {
- priv->sgIF = 108;
priv->tda8290_easy_mode = 0x02;
mode = "B";
} else if (params->std & V4L2_STD_GH) {
- priv->sgIF = 124;
priv->tda8290_easy_mode = 0x04;
mode = "GH";
} else if (params->std & V4L2_STD_PAL_I) {
- priv->sgIF = 124;
priv->tda8290_easy_mode = 0x08;
mode = "I";
} else if (params->std & V4L2_STD_DK) {
- priv->sgIF = 124;
priv->tda8290_easy_mode = 0x10;
mode = "DK";
} else if (params->std & V4L2_STD_SECAM_L) {
- priv->sgIF = 124;
priv->tda8290_easy_mode = 0x20;
mode = "L";
} else if (params->std & V4L2_STD_SECAM_LC) {
- priv->sgIF = 20;
priv->tda8290_easy_mode = 0x40;
mode = "LC";
} else {
- priv->sgIF = 124;
priv->tda8290_easy_mode = 0x10;
mode = "xx";
}
- if (params->mode == V4L2_TUNER_RADIO)
- priv->sgIF = 88; /* if frequency is 5.5 MHz */
-
- tuner_dbg("setting tda8290 to system %s\n", mode);
+ tuner_dbg("setting tda829x to system %s\n", mode);
}
-static int tda8290_set_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
+static void tda8290_set_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
{
- struct tda8290_priv *priv = fe->tuner_priv;
+ struct tda8290_priv *priv = fe->analog_demod_priv;
+
unsigned char soft_reset[] = { 0x00, 0x00 };
unsigned char easy_mode[] = { 0x01, priv->tda8290_easy_mode };
unsigned char expert_mode[] = { 0x01, 0x80 };
@@ -457,8 +173,8 @@ static int tda8290_set_params(struct dvb_frontend *fe,
set_audio(fe, params);
- if (priv->lna_cfg)
- tuner_dbg("tda827xa config is 0x%02x\n", *priv->lna_cfg);
+ if (priv->cfg.config)
+ tuner_dbg("tda827xa config is 0x%02x\n", *priv->cfg.config);
tuner_i2c_xfer_send(&priv->i2c_props, easy_mode, 2);
tuner_i2c_xfer_send(&priv->i2c_props, agc_out_on, 2);
tuner_i2c_xfer_send(&priv->i2c_props, soft_reset, 2);
@@ -475,10 +191,10 @@ static int tda8290_set_params(struct dvb_frontend *fe,
tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2);
tda8290_i2c_bridge(fe, 1);
- if (priv->tda827x_ver != 0)
- tda827xa_set_analog_params(fe, params);
- else
- tda827x_set_analog_params(fe, params);
+
+ if (fe->ops.tuner_ops.set_analog_params)
+ fe->ops.tuner_ops.set_analog_params(fe, params);
+
for (i = 0; i < 3; i++) {
tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1);
tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1);
@@ -507,10 +223,8 @@ static int tda8290_set_params(struct dvb_frontend *fe,
if ((agc_stat > 115) || !(pll_stat & 0x80)) {
tuner_dbg("adjust gain, step 2. Agc: %d, lock: %d\n",
agc_stat, pll_stat & 0x80);
- if (priv->tda827x_ver != 0)
- tda827xa_agcf(fe);
- else
- tda827x_agcf(fe);
+ if (priv->cfg.agcf)
+ priv->cfg.agcf(fe);
msleep(100);
tuner_i2c_xfer_send(&priv->i2c_props, &addr_agc_stat, 1);
tuner_i2c_xfer_recv(&priv->i2c_props, &agc_stat, 1);
@@ -541,99 +255,242 @@ static int tda8290_set_params(struct dvb_frontend *fe,
tda8290_i2c_bridge(fe, 0);
tuner_i2c_xfer_send(&priv->i2c_props, if_agc_set, 2);
+}
+
+/*---------------------------------------------------------------------*/
+
+static void tda8295_power(struct dvb_frontend *fe, int enable)
+{
+ struct tda8290_priv *priv = fe->analog_demod_priv;
+ unsigned char buf[] = { 0x30, 0x00 }; /* clb_stdbt */
- priv->frequency = (V4L2_TUNER_RADIO == params->mode) ?
- params->frequency * 125 / 2 : params->frequency * 62500;
+ tuner_i2c_xfer_send(&priv->i2c_props, &buf[0], 1);
+ tuner_i2c_xfer_recv(&priv->i2c_props, &buf[1], 1);
- return 0;
+ if (enable)
+ buf[1] = 0x01;
+ else
+ buf[1] = 0x03;
+
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
+}
+
+static void tda8295_set_easy_mode(struct dvb_frontend *fe, int enable)
+{
+ struct tda8290_priv *priv = fe->analog_demod_priv;
+ unsigned char buf[] = { 0x01, 0x00 };
+
+ tuner_i2c_xfer_send(&priv->i2c_props, &buf[0], 1);
+ tuner_i2c_xfer_recv(&priv->i2c_props, &buf[1], 1);
+
+ if (enable)
+ buf[1] = 0x01; /* rising edge sets regs 0x02 - 0x23 */
+ else
+ buf[1] = 0x00; /* reset active bit */
+
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
+}
+
+static void tda8295_set_video_std(struct dvb_frontend *fe)
+{
+ struct tda8290_priv *priv = fe->analog_demod_priv;
+ unsigned char buf[] = { 0x00, priv->tda8290_easy_mode };
+
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
+
+ tda8295_set_easy_mode(fe, 1);
+ msleep(20);
+ tda8295_set_easy_mode(fe, 0);
}
/*---------------------------------------------------------------------*/
-static int tda8290_has_signal(struct dvb_frontend *fe)
+static void tda8295_agc1_out(struct dvb_frontend *fe, int enable)
{
- struct tda8290_priv *priv = fe->tuner_priv;
- int ret;
+ struct tda8290_priv *priv = fe->analog_demod_priv;
+ unsigned char buf[] = { 0x02, 0x00 }; /* DIV_FUNC */
- unsigned char i2c_get_afc[1] = { 0x1B };
- unsigned char afc = 0;
+ tuner_i2c_xfer_send(&priv->i2c_props, &buf[0], 1);
+ tuner_i2c_xfer_recv(&priv->i2c_props, &buf[1], 1);
- /* for now, report based on afc status */
- tuner_i2c_xfer_send(&priv->i2c_props, i2c_get_afc, ARRAY_SIZE(i2c_get_afc));
- tuner_i2c_xfer_recv(&priv->i2c_props, &afc, 1);
+ if (enable)
+ buf[1] &= ~0x40;
+ else
+ buf[1] |= 0x40;
- ret = (afc & 0x80) ? 65535 : 0;
+ tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
+}
+
+static void tda8295_agc2_out(struct dvb_frontend *fe, int enable)
+{
+ struct tda8290_priv *priv = fe->analog_demod_priv;
+ unsigned char set_gpio_cf[] = { 0x44, 0x00 };
+ unsigned char set_gpio_val[] = { 0x46, 0x00 };
+
+ tuner_i2c_xfer_send(&priv->i2c_props, &set_gpio_cf[0], 1);
+ tuner_i2c_xfer_recv(&priv->i2c_props, &set_gpio_cf[1], 1);
+ tuner_i2c_xfer_send(&priv->i2c_props, &set_gpio_val[0], 1);
+ tuner_i2c_xfer_recv(&priv->i2c_props, &set_gpio_val[1], 1);
- tuner_dbg("AFC status: %d\n", ret);
+ set_gpio_cf[1] &= 0xf0; /* clear GPIO_0 bits 3-0 */
- return ret;
+ if (enable) {
+ set_gpio_cf[1] |= 0x01; /* config GPIO_0 as Open Drain Out */
+ set_gpio_val[1] &= 0xfe; /* set GPIO_0 pin low */
+ }
+ tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_cf, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_val, 2);
}
-static int tda8290_get_status(struct dvb_frontend *fe, u32 *status)
+static int tda8295_has_signal(struct dvb_frontend *fe)
{
- *status = 0;
+ struct tda8290_priv *priv = fe->analog_demod_priv;
- if (tda8290_has_signal(fe))
- *status = TUNER_STATUS_LOCKED;
+ unsigned char hvpll_stat = 0x26;
+ unsigned char ret;
- return 0;
+ tuner_i2c_xfer_send(&priv->i2c_props, &hvpll_stat, 1);
+ tuner_i2c_xfer_recv(&priv->i2c_props, &ret, 1);
+ return (ret & 0x01) ? 65535 : 0;
}
-static int tda8290_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
+/*---------------------------------------------------------------------*/
+
+static void tda8295_set_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
{
- *strength = tda8290_has_signal(fe);
+ struct tda8290_priv *priv = fe->analog_demod_priv;
- return 0;
+ unsigned char blanking_mode[] = { 0x1d, 0x00 };
+
+ set_audio(fe, params);
+
+ tuner_dbg("%s: freq = %d\n", __FUNCTION__, params->frequency);
+
+ tda8295_power(fe, 1);
+ tda8295_agc1_out(fe, 1);
+
+ tuner_i2c_xfer_send(&priv->i2c_props, &blanking_mode[0], 1);
+ tuner_i2c_xfer_recv(&priv->i2c_props, &blanking_mode[1], 1);
+
+ tda8295_set_video_std(fe);
+
+ blanking_mode[1] = 0x03;
+ tuner_i2c_xfer_send(&priv->i2c_props, blanking_mode, 2);
+ msleep(20);
+
+ tda8295_i2c_bridge(fe, 1);
+
+ if (fe->ops.tuner_ops.set_analog_params)
+ fe->ops.tuner_ops.set_analog_params(fe, params);
+
+ if (priv->cfg.agcf)
+ priv->cfg.agcf(fe);
+
+ if (tda8295_has_signal(fe))
+ tuner_dbg("tda8295 is locked\n");
+ else
+ tuner_dbg("tda8295 not locked, no signal?\n");
+
+ tda8295_i2c_bridge(fe, 0);
+}
+
+/*---------------------------------------------------------------------*/
+
+static int tda8290_has_signal(struct dvb_frontend *fe)
+{
+ struct tda8290_priv *priv = fe->analog_demod_priv;
+
+ unsigned char i2c_get_afc[1] = { 0x1B };
+ unsigned char afc = 0;
+
+ tuner_i2c_xfer_send(&priv->i2c_props, i2c_get_afc, ARRAY_SIZE(i2c_get_afc));
+ tuner_i2c_xfer_recv(&priv->i2c_props, &afc, 1);
+ return (afc & 0x80)? 65535:0;
}
/*---------------------------------------------------------------------*/
-static int tda8290_standby(struct dvb_frontend *fe)
+static void tda8290_standby(struct dvb_frontend *fe)
{
- struct tda8290_priv *priv = fe->tuner_priv;
+ struct tda8290_priv *priv = fe->analog_demod_priv;
+
unsigned char cb1[] = { 0x30, 0xD0 };
unsigned char tda8290_standby[] = { 0x00, 0x02 };
unsigned char tda8290_agc_tri[] = { 0x02, 0x20 };
struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, .buf=cb1, .len = 2};
tda8290_i2c_bridge(fe, 1);
- if (priv->tda827x_ver != 0)
+ if (priv->ver & TDA8275A)
cb1[1] = 0x90;
i2c_transfer(priv->i2c_props.adap, &msg, 1);
tda8290_i2c_bridge(fe, 0);
tuner_i2c_xfer_send(&priv->i2c_props, tda8290_agc_tri, 2);
tuner_i2c_xfer_send(&priv->i2c_props, tda8290_standby, 2);
-
- return 0;
}
+static void tda8295_standby(struct dvb_frontend *fe)
+{
+ tda8295_agc1_out(fe, 0); /* Put AGC in tri-state */
+
+ tda8295_power(fe, 0);
+}
static void tda8290_init_if(struct dvb_frontend *fe)
{
- struct tda8290_priv *priv = fe->tuner_priv;
+ struct tda8290_priv *priv = fe->analog_demod_priv;
unsigned char set_VS[] = { 0x30, 0x6F };
unsigned char set_GP00_CF[] = { 0x20, 0x01 };
unsigned char set_GP01_CF[] = { 0x20, 0x0B };
- if ((priv->lna_cfg) &&
- ((*priv->lna_cfg == 1) || (*priv->lna_cfg == 2)))
+ if ((priv->cfg.config) &&
+ ((*priv->cfg.config == 1) || (*priv->cfg.config == 2)))
tuner_i2c_xfer_send(&priv->i2c_props, set_GP00_CF, 2);
else
tuner_i2c_xfer_send(&priv->i2c_props, set_GP01_CF, 2);
tuner_i2c_xfer_send(&priv->i2c_props, set_VS, 2);
}
+static void tda8295_init_if(struct dvb_frontend *fe)
+{
+ struct tda8290_priv *priv = fe->analog_demod_priv;
+
+ static unsigned char set_adc_ctl[] = { 0x33, 0x14 };
+ static unsigned char set_adc_ctl2[] = { 0x34, 0x00 };
+ static unsigned char set_pll_reg6[] = { 0x3e, 0x63 };
+ static unsigned char set_pll_reg0[] = { 0x38, 0x23 };
+ static unsigned char set_pll_reg7[] = { 0x3f, 0x01 };
+ static unsigned char set_pll_reg10[] = { 0x42, 0x61 };
+ static unsigned char set_gpio_reg0[] = { 0x44, 0x0b };
+
+ tda8295_power(fe, 1);
+
+ tda8295_set_easy_mode(fe, 0);
+ tda8295_set_video_std(fe);
+
+ tuner_i2c_xfer_send(&priv->i2c_props, set_adc_ctl, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, set_adc_ctl2, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg6, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg0, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg7, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg10, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_reg0, 2);
+
+ tda8295_agc1_out(fe, 0);
+ tda8295_agc2_out(fe, 0);
+}
+
static void tda8290_init_tuner(struct dvb_frontend *fe)
{
- struct tda8290_priv *priv = fe->tuner_priv;
+ struct tda8290_priv *priv = fe->analog_demod_priv;
unsigned char tda8275_init[] = { 0x00, 0x00, 0x00, 0x40, 0xdC, 0x04, 0xAf,
0x3F, 0x2A, 0x04, 0xFF, 0x00, 0x00, 0x40 };
unsigned char tda8275a_init[] = { 0x00, 0x00, 0x00, 0x00, 0xdC, 0x05, 0x8b,
0x0c, 0x04, 0x20, 0xFF, 0x00, 0x00, 0x4b };
struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0,
.buf=tda8275_init, .len = 14};
- if (priv->tda827x_ver != 0)
+ if (priv->ver & TDA8275A)
msg.buf = tda8275a_init;
tda8290_i2c_bridge(fe, 1);
@@ -643,58 +500,42 @@ static void tda8290_init_tuner(struct dvb_frontend *fe)
/*---------------------------------------------------------------------*/
-static int tda8290_release(struct dvb_frontend *fe)
+static void tda829x_release(struct dvb_frontend *fe)
{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
+ struct tda8290_priv *priv = fe->analog_demod_priv;
- return 0;
-}
+ /* only try to release the tuner if we've
+ * attached it from within this module */
+ if (priv->ver & (TDA18271 | TDA8275 | TDA8275A))
+ if (fe->ops.tuner_ops.release)
+ fe->ops.tuner_ops.release(fe);
-static int tda8290_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct tda8290_priv *priv = fe->tuner_priv;
- *frequency = priv->frequency;
- return 0;
+ kfree(fe->analog_demod_priv);
+ fe->analog_demod_priv = NULL;
}
-static struct dvb_tuner_ops tda8290_tuner_ops = {
- .sleep = tda8290_standby,
- .set_analog_params = tda8290_set_params,
- .release = tda8290_release,
- .get_frequency = tda8290_get_frequency,
- .get_status = tda8290_get_status,
- .get_rf_strength = tda8290_get_rf_strength,
+static struct tda18271_config tda829x_tda18271_config = {
+ .gate = TDA18271_GATE_ANALOG,
};
-struct dvb_frontend *tda8290_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr,
- struct tda8290_config *cfg)
+static int tda829x_find_tuner(struct dvb_frontend *fe)
{
- struct tda8290_priv *priv = NULL;
- u8 data;
+ struct tda8290_priv *priv = fe->analog_demod_priv;
+ struct analog_demod_ops *analog_ops = &fe->ops.analog_ops;
int i, ret, tuners_found;
u32 tuner_addrs;
- struct i2c_msg msg = {.flags=I2C_M_RD, .buf=&data, .len = 1};
+ u8 data;
+ struct i2c_msg msg = { .flags = I2C_M_RD, .buf = &data, .len = 1 };
- priv = kzalloc(sizeof(struct tda8290_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
- fe->tuner_priv = priv;
+ if (NULL == analog_ops->i2c_gate_ctrl)
+ return -EINVAL;
- priv->i2c_props.addr = i2c_addr;
- priv->i2c_props.adap = i2c_adap;
- if (cfg) {
- priv->lna_cfg = cfg->lna_cfg;
- priv->tuner_callback = cfg->tuner_callback;
- }
+ analog_ops->i2c_gate_ctrl(fe, 1);
- tda8290_i2c_bridge(fe, 1);
/* probe for tuner chip */
tuners_found = 0;
tuner_addrs = 0;
- for (i=0x60; i<= 0x63; i++) {
+ for (i = 0x60; i <= 0x63; i++) {
msg.addr = i;
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
if (ret == 1) {
@@ -706,20 +547,23 @@ struct dvb_frontend *tda8290_attach(struct dvb_frontend *fe,
behind the bridge and we choose the highest address that doesn't
give a response now
*/
- tda8290_i2c_bridge(fe, 0);
- if(tuners_found > 1)
+
+ analog_ops->i2c_gate_ctrl(fe, 0);
+
+ if (tuners_found > 1)
for (i = 0; i < tuners_found; i++) {
msg.addr = tuner_addrs & 0xff;
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
- if(ret == 1)
+ if (ret == 1)
tuner_addrs = tuner_addrs >> 8;
else
break;
}
+
if (tuner_addrs == 0) {
- tuner_addrs = 0x61;
- tuner_info("could not clearly identify tuner address, defaulting to %x\n",
- tuner_addrs);
+ tuner_addrs = 0x60;
+ tuner_info("could not clearly identify tuner address, "
+ "defaulting to %x\n", tuner_addrs);
} else {
tuner_addrs = tuner_addrs & 0xff;
tuner_info("setting tuner address to %x\n", tuner_addrs);
@@ -727,42 +571,181 @@ struct dvb_frontend *tda8290_attach(struct dvb_frontend *fe,
priv->tda827x_addr = tuner_addrs;
msg.addr = tuner_addrs;
- tda8290_i2c_bridge(fe, 1);
+ analog_ops->i2c_gate_ctrl(fe, 1);
ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
- if( ret != 1)
- tuner_warn("TDA827x access failed!\n");
-
- memcpy(&fe->ops.tuner_ops, &tda8290_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- if ((data & 0x3c) == 0) {
- strlcpy(fe->ops.tuner_ops.info.name, "tda8290+75",
- sizeof(fe->ops.tuner_ops.info.name));
- fe->ops.tuner_ops.info.frequency_min = 55000000;
- fe->ops.tuner_ops.info.frequency_max = 860000000;
- fe->ops.tuner_ops.info.frequency_step = 250000;
- priv->tda827x_ver = 0;
+
+ if (ret != 1) {
+ tuner_warn("tuner access failed!\n");
+ return -EREMOTEIO;
+ }
+
+ if ((data == 0x83) || (data == 0x84)) {
+ priv->ver |= TDA18271;
+ tda18271_attach(fe, priv->tda827x_addr,
+ priv->i2c_props.adap,
+ &tda829x_tda18271_config);
} else {
- strlcpy(fe->ops.tuner_ops.info.name, "tda8290+75a",
- sizeof(fe->ops.tuner_ops.info.name));
- fe->ops.tuner_ops.info.frequency_min = 44000000;
- fe->ops.tuner_ops.info.frequency_max = 906000000;
- fe->ops.tuner_ops.info.frequency_step = 62500;
- priv->tda827x_ver = 2;
+ if ((data & 0x3c) == 0)
+ priv->ver |= TDA8275;
+ else
+ priv->ver |= TDA8275A;
+
+ tda827x_attach(fe, priv->tda827x_addr,
+ priv->i2c_props.adap, &priv->cfg);
+ }
+ if (fe->ops.tuner_ops.init)
+ fe->ops.tuner_ops.init(fe);
+
+ if (fe->ops.tuner_ops.sleep)
+ fe->ops.tuner_ops.sleep(fe);
+
+ analog_ops->i2c_gate_ctrl(fe, 0);
+
+ return 0;
+}
+
+static int tda8290_probe(struct tuner_i2c_props *i2c_props)
+{
+#define TDA8290_ID 0x89
+ unsigned char tda8290_id[] = { 0x1f, 0x00 };
+
+ /* detect tda8290 */
+ tuner_i2c_xfer_send(i2c_props, &tda8290_id[0], 1);
+ tuner_i2c_xfer_recv(i2c_props, &tda8290_id[1], 1);
+
+ if (tda8290_id[1] == TDA8290_ID) {
+ if (debug)
+ printk(KERN_DEBUG "%s: tda8290 detected @ %d-%04x\n",
+ __FUNCTION__, i2c_adapter_id(i2c_props->adap),
+ i2c_props->addr);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int tda8295_probe(struct tuner_i2c_props *i2c_props)
+{
+#define TDA8295_ID 0x8a
+ unsigned char tda8295_id[] = { 0x2f, 0x00 };
+
+ /* detect tda8295 */
+ tuner_i2c_xfer_send(i2c_props, &tda8295_id[0], 1);
+ tuner_i2c_xfer_recv(i2c_props, &tda8295_id[1], 1);
+
+ if (tda8295_id[1] == TDA8295_ID) {
+ if (debug)
+ printk(KERN_DEBUG "%s: tda8295 detected @ %d-%04x\n",
+ __FUNCTION__, i2c_adapter_id(i2c_props->adap),
+ i2c_props->addr);
+ return 0;
}
- priv->tda827x_lpsel = 0;
+ return -ENODEV;
+}
+
+static struct analog_demod_ops tda8290_ops = {
+ .set_params = tda8290_set_params,
+ .has_signal = tda8290_has_signal,
+ .standby = tda8290_standby,
+ .release = tda829x_release,
+ .i2c_gate_ctrl = tda8290_i2c_bridge,
+};
+
+static struct analog_demod_ops tda8295_ops = {
+ .set_params = tda8295_set_params,
+ .has_signal = tda8295_has_signal,
+ .standby = tda8295_standby,
+ .release = tda829x_release,
+ .i2c_gate_ctrl = tda8295_i2c_bridge,
+};
+
+struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c_adap, u8 i2c_addr,
+ struct tda829x_config *cfg)
+{
+ struct tda8290_priv *priv = NULL;
+ char *name;
+
+ priv = kzalloc(sizeof(struct tda8290_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+ fe->analog_demod_priv = priv;
+
+ priv->i2c_props.addr = i2c_addr;
+ priv->i2c_props.adap = i2c_adap;
+ if (cfg) {
+ priv->cfg.config = cfg->lna_cfg;
+ priv->cfg.tuner_callback = cfg->tuner_callback;
+ }
+
+ if (tda8290_probe(&priv->i2c_props) == 0) {
+ priv->ver = TDA8290;
+ memcpy(&fe->ops.analog_ops, &tda8290_ops,
+ sizeof(struct analog_demod_ops));
+ }
+
+ if (tda8295_probe(&priv->i2c_props) == 0) {
+ priv->ver = TDA8295;
+ memcpy(&fe->ops.analog_ops, &tda8295_ops,
+ sizeof(struct analog_demod_ops));
+ }
+
+ if ((!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) &&
+ (tda829x_find_tuner(fe) < 0))
+ goto fail;
+
+ switch (priv->ver) {
+ case TDA8290:
+ name = "tda8290";
+ break;
+ case TDA8295:
+ name = "tda8295";
+ break;
+ case TDA8290 | TDA8275:
+ name = "tda8290+75";
+ break;
+ case TDA8295 | TDA8275:
+ name = "tda8295+75";
+ break;
+ case TDA8290 | TDA8275A:
+ name = "tda8290+75a";
+ break;
+ case TDA8295 | TDA8275A:
+ name = "tda8295+75a";
+ break;
+ case TDA8290 | TDA18271:
+ name = "tda8290+18271";
+ break;
+ case TDA8295 | TDA18271:
+ name = "tda8295+18271";
+ break;
+ default:
+ goto fail;
+ }
+ tuner_info("type set to %s\n", name);
+
+ fe->ops.analog_ops.info.name = name;
+
+ if (priv->ver & TDA8290) {
+ tda8290_init_tuner(fe);
+ tda8290_init_if(fe);
+ } else if (priv->ver & TDA8295)
+ tda8295_init_if(fe);
- tda8290_init_tuner(fe);
- tda8290_init_if(fe);
return fe;
+
+fail:
+ tda829x_release(fe);
+ return NULL;
}
+EXPORT_SYMBOL_GPL(tda829x_attach);
-int tda8290_probe(struct i2c_adapter* i2c_adap, u8 i2c_addr)
+int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr)
{
struct tuner_i2c_props i2c_props = {
.adap = i2c_adap,
- .addr = i2c_addr
+ .addr = i2c_addr,
};
unsigned char soft_reset[] = { 0x00, 0x00 };
@@ -771,7 +754,27 @@ int tda8290_probe(struct i2c_adapter* i2c_adap, u8 i2c_addr)
unsigned char restore_9886[] = { 0x00, 0xd6, 0x30 };
unsigned char addr_dto_lsb = 0x07;
unsigned char data;
+#define PROBE_BUFFER_SIZE 8
+ unsigned char buf[PROBE_BUFFER_SIZE];
+ int i;
+
+ /* rule out tda9887, which would return the same byte repeatedly */
+ tuner_i2c_xfer_send(&i2c_props, soft_reset, 1);
+ tuner_i2c_xfer_recv(&i2c_props, buf, PROBE_BUFFER_SIZE);
+ for (i = 1; i < PROBE_BUFFER_SIZE; i++) {
+ if (buf[i] != buf[0])
+ break;
+ }
+ /* all bytes are equal, not a tda829x - probably a tda9887 */
+ if (i == PROBE_BUFFER_SIZE)
+ return -ENODEV;
+
+ if ((tda8290_probe(&i2c_props) == 0) ||
+ (tda8295_probe(&i2c_props) == 0))
+ return 0;
+
+ /* fall back to old probing method */
tuner_i2c_xfer_send(&i2c_props, easy_mode_b, 2);
tuner_i2c_xfer_send(&i2c_props, soft_reset, 2);
tuner_i2c_xfer_send(&i2c_props, &addr_dto_lsb, 1);
@@ -786,14 +789,12 @@ int tda8290_probe(struct i2c_adapter* i2c_adap, u8 i2c_addr)
}
}
tuner_i2c_xfer_send(&i2c_props, restore_9886, 3);
- return -1;
+ return -ENODEV;
}
+EXPORT_SYMBOL_GPL(tda829x_probe);
-EXPORT_SYMBOL_GPL(tda8290_probe);
-EXPORT_SYMBOL_GPL(tda8290_attach);
-
-MODULE_DESCRIPTION("Philips TDA8290 + TDA8275 / TDA8275a tuner driver");
-MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann");
+MODULE_DESCRIPTION("Philips/NXP TDA8290/TDA8295 analog IF demodulator driver");
+MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann, Michael Krufky");
MODULE_LICENSE("GPL");
/*
diff --git a/drivers/media/video/tda8290.h b/drivers/media/video/tda8290.h
index 107b24b05aa1..dc8ef310b7b2 100644
--- a/drivers/media/video/tda8290.h
+++ b/drivers/media/video/tda8290.h
@@ -20,33 +20,36 @@
#include <linux/i2c.h>
#include "dvb_frontend.h"
-struct tda8290_config
-{
+struct tda829x_config {
unsigned int *lna_cfg;
- int (*tuner_callback) (void *dev, int command,int arg);
+ int (*tuner_callback) (void *dev, int command, int arg);
+
+ unsigned int probe_tuner:1;
+#define TDA829X_PROBE_TUNER 0
+#define TDA829X_DONT_PROBE 1
};
#if defined(CONFIG_TUNER_TDA8290) || (defined(CONFIG_TUNER_TDA8290_MODULE) && defined(MODULE))
-extern int tda8290_probe(struct i2c_adapter* i2c_adap, u8 i2c_addr);
+extern int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr);
-extern struct dvb_frontend *tda8290_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
+extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c_adap,
u8 i2c_addr,
- struct tda8290_config *cfg);
+ struct tda829x_config *cfg);
#else
-static inline int tda8290_probe(struct i2c_adapter* i2c_adap, u8 i2c_addr)
+static inline int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr)
{
- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
- __FUNCTION__);
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
return -EINVAL;
}
-static inline struct dvb_frontend *tda8290_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
+static inline struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c_adap,
u8 i2c_addr,
- struct tda8290_config *cfg)
+ struct tda829x_config *cfg)
{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
+ __FUNCTION__);
return NULL;
}
#endif
diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c
index d11044170872..3c0557130a70 100644
--- a/drivers/media/video/tda9875.c
+++ b/drivers/media/video/tda9875.c
@@ -7,6 +7,7 @@
*
* Copyright (c) 2000 Guillaume Delvit based on Gerd Knorr source and
* Eric Sandeen
+ * Copyright (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
* This code is placed under the terms of the GNU General Public License
* Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu)
* Which was based on tda8425.c by Greg Alexander (c) 1998
@@ -268,87 +269,143 @@ static int tda9875_detach(struct i2c_client *client)
return 0;
}
-static int tda9875_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tda9875_get_ctrl(struct i2c_client *client,
+ struct v4l2_control *ctrl)
{
struct tda9875 *t = i2c_get_clientdata(client);
- dprintk("In tda9875_command...\n");
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ {
+ int left = (t->lvol+84)*606;
+ int right = (t->rvol+84)*606;
- switch (cmd) {
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOCGAUDIO:
+ ctrl->value=max(left,right);
+ return 0;
+ }
+ case V4L2_CID_AUDIO_BALANCE:
{
- struct video_audio *va = arg;
- int left,right;
+ int left = (t->lvol+84)*606;
+ int right = (t->rvol+84)*606;
+ int volume = max(left,right);
+ int balance = (32768*min(left,right))/
+ (volume ? volume : 1);
+ ctrl->value=(left<right)?
+ (65535-balance) : balance;
+ return 0;
+ }
+ case V4L2_CID_AUDIO_BASS:
+ ctrl->value = (t->bass+12)*2427; /* min -12 max +15 */
+ return 0;
+ case V4L2_CID_AUDIO_TREBLE:
+ ctrl->value = (t->treble+12)*2730;/* min -12 max +12 */
+ return 0;
+ }
+ return -EINVAL;
+}
- dprintk("VIDIOCGAUDIO\n");
+static int tda9875_set_ctrl(struct i2c_client *client,
+ struct v4l2_control *ctrl)
+{
+ struct tda9875 *t = i2c_get_clientdata(client);
+ int chvol=0, volume, balance, left, right;
- va->flags |= VIDEO_AUDIO_VOLUME |
- VIDEO_AUDIO_BASS |
- VIDEO_AUDIO_TREBLE;
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ left = (t->lvol+84)*606;
+ right = (t->rvol+84)*606;
+
+ volume = max(left,right);
+ balance = (32768*min(left,right))/
+ (volume ? volume : 1);
+ balance =(left<right)?
+ (65535-balance) : balance;
+
+ volume = ctrl->value;
- /* min is -84 max is 24 */
+ chvol=1;
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
left = (t->lvol+84)*606;
right = (t->rvol+84)*606;
- va->volume=max(left,right);
- va->balance=(32768*min(left,right))/
- (va->volume ? va->volume : 1);
- va->balance=(left<right)?
- (65535-va->balance) : va->balance;
- va->bass = (t->bass+12)*2427; /* min -12 max +15 */
- va->treble = (t->treble+12)*2730;/* min -12 max +12 */
- va->mode |= VIDEO_SOUND_MONO;
-
- break; /* VIDIOCGAUDIO case */
+
+ volume=max(left,right);
+
+ balance = ctrl->value;
+
+ chvol=1;
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ t->bass = ((ctrl->value/2400)-12) & 0xff;
+ if (t->bass > 15)
+ t->bass = 15;
+ if (t->bass < -12)
+ t->bass = -12 & 0xff;
+ break;
+ case V4L2_CID_AUDIO_TREBLE:
+ t->treble = ((ctrl->value/2700)-12) & 0xff;
+ if (t->treble > 12)
+ t->treble = 12;
+ if (t->treble < -12)
+ t->treble = -12 & 0xff;
+ break;
+ default:
+ return -EINVAL;
}
- case VIDIOCSAUDIO:
- {
- struct video_audio *va = arg;
- int left,right;
-
- dprintk("VIDEOCSAUDIO...\n");
- left = (min(65536 - va->balance,32768) *
- va->volume) / 32768;
- right = (min(va->balance,(__u16)32768) *
- va->volume) / 32768;
+ if (chvol) {
+ left = (min(65536 - balance,32768) *
+ volume) / 32768;
+ right = (min(balance,32768) *
+ volume) / 32768;
t->lvol = ((left/606)-84) & 0xff;
if (t->lvol > 24)
- t->lvol = 24;
+ t->lvol = 24;
if (t->lvol < -84)
- t->lvol = -84 & 0xff;
+ t->lvol = -84 & 0xff;
t->rvol = ((right/606)-84) & 0xff;
if (t->rvol > 24)
- t->rvol = 24;
+ t->rvol = 24;
if (t->rvol < -84)
- t->rvol = -84 & 0xff;
-
- t->bass = ((va->bass/2400)-12) & 0xff;
- if (t->bass > 15)
- t->bass = 15;
- if (t->bass < -12)
- t->bass = -12 & 0xff;
-
- t->treble = ((va->treble/2700)-12) & 0xff;
- if (t->treble > 12)
- t->treble = 12;
- if (t->treble < -12)
- t->treble = -12 & 0xff;
+ t->rvol = -84 & 0xff;
+ }
+//printk("tda9875 bal:%04x vol:%04x bass:%04x treble:%04x\n",va->balance,va->volume,va->bass,va->treble);
+ tda9875_set(client);
-//printk("tda9875 bal:%04x vol:%04x bass:%04x treble:%04x\n",va->balance,va->volume,va->bass,va->treble);
+ return 0;
+}
- tda9875_set(client);
+static int tda9875_command(struct i2c_client *client,
+ unsigned int cmd, void *arg)
+{
+ dprintk("In tda9875_command...\n");
- break;
+ switch (cmd) {
+ /* --- v4l ioctls --- */
+ /* take care: bttv does userspace copying, we'll get a
+ kernel pointer here... */
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ default:
+ return -EINVAL;
+ }
+ return v4l2_ctrl_query_fill_std(qc);
+ }
+ case VIDIOC_S_CTRL:
+ return tda9875_set_ctrl(client, arg);
- } /* end of VIDEOCSAUDIO case */
+ case VIDIOC_G_CTRL:
+ return tda9875_get_ctrl(client, arg);
default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */
diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c
index be5387f11afb..106c93b8203f 100644
--- a/drivers/media/video/tda9887.c
+++ b/drivers/media/video/tda9887.c
@@ -9,7 +9,8 @@
#include <linux/videodev.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
-#include "tuner-driver.h"
+#include "tuner-i2c.h"
+#include "tda9887.h"
/* Chips:
@@ -20,18 +21,20 @@
Used as part of several tuners
*/
-#define tda9887_info(fmt, arg...) do {\
- printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
- i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
-#define tda9887_dbg(fmt, arg...) do {\
- if (tuner_debug) \
- printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
- i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable verbose debug messages");
+
+#define PREFIX "tda9887"
struct tda9887_priv {
struct tuner_i2c_props i2c_props;
unsigned char data[4];
+ unsigned int config;
+ unsigned int mode;
+ unsigned int audmode;
+ v4l2_std_id std;
};
/* ---------------------------------------------------------------------- */
@@ -262,8 +265,10 @@ static struct tvnorm radio_mono = {
/* ---------------------------------------------------------------------- */
-static void dump_read_message(struct tuner *t, unsigned char *buf)
+static void dump_read_message(struct dvb_frontend *fe, unsigned char *buf)
{
+ struct tda9887_priv *priv = fe->analog_demod_priv;
+
static char *afc[16] = {
"- 12.5 kHz",
"- 37.5 kHz",
@@ -282,16 +287,18 @@ static void dump_read_message(struct tuner *t, unsigned char *buf)
"+ 37.5 kHz",
"+ 12.5 kHz",
};
- tda9887_info("read: 0x%2x\n", buf[0]);
- tda9887_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
- tda9887_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]);
- tda9887_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low");
- tda9887_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out");
- tda9887_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low");
+ tuner_info("read: 0x%2x\n", buf[0]);
+ tuner_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
+ tuner_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]);
+ tuner_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low");
+ tuner_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out");
+ tuner_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low");
}
-static void dump_write_message(struct tuner *t, unsigned char *buf)
+static void dump_write_message(struct dvb_frontend *fe, unsigned char *buf)
{
+ struct tda9887_priv *priv = fe->analog_demod_priv;
+
static char *sound[4] = {
"AM/TV",
"FM/radio",
@@ -330,86 +337,90 @@ static void dump_write_message(struct tuner *t, unsigned char *buf)
"44 MHz",
};
- tda9887_info("write: byte B 0x%02x\n",buf[1]);
- tda9887_info(" B0 video mode : %s\n",
- (buf[1] & 0x01) ? "video trap" : "sound trap");
- tda9887_info(" B1 auto mute fm : %s\n",
- (buf[1] & 0x02) ? "yes" : "no");
- tda9887_info(" B2 carrier mode : %s\n",
- (buf[1] & 0x04) ? "QSS" : "Intercarrier");
- tda9887_info(" B3-4 tv sound/radio : %s\n",
- sound[(buf[1] & 0x18) >> 3]);
- tda9887_info(" B5 force mute audio: %s\n",
- (buf[1] & 0x20) ? "yes" : "no");
- tda9887_info(" B6 output port 1 : %s\n",
- (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
- tda9887_info(" B7 output port 2 : %s\n",
- (buf[1] & 0x80) ? "high (inactive)" : "low (active)");
-
- tda9887_info("write: byte C 0x%02x\n",buf[2]);
- tda9887_info(" C0-4 top adjustment : %s dB\n", adjust[buf[2] & 0x1f]);
- tda9887_info(" C5-6 de-emphasis : %s\n", deemph[(buf[2] & 0x60) >> 5]);
- tda9887_info(" C7 audio gain : %s\n",
- (buf[2] & 0x80) ? "-6" : "0");
-
- tda9887_info("write: byte E 0x%02x\n",buf[3]);
- tda9887_info(" E0-1 sound carrier : %s\n",
- carrier[(buf[3] & 0x03)]);
- tda9887_info(" E6 l pll gating : %s\n",
- (buf[3] & 0x40) ? "36" : "13");
+ tuner_info("write: byte B 0x%02x\n", buf[1]);
+ tuner_info(" B0 video mode : %s\n",
+ (buf[1] & 0x01) ? "video trap" : "sound trap");
+ tuner_info(" B1 auto mute fm : %s\n",
+ (buf[1] & 0x02) ? "yes" : "no");
+ tuner_info(" B2 carrier mode : %s\n",
+ (buf[1] & 0x04) ? "QSS" : "Intercarrier");
+ tuner_info(" B3-4 tv sound/radio : %s\n",
+ sound[(buf[1] & 0x18) >> 3]);
+ tuner_info(" B5 force mute audio: %s\n",
+ (buf[1] & 0x20) ? "yes" : "no");
+ tuner_info(" B6 output port 1 : %s\n",
+ (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
+ tuner_info(" B7 output port 2 : %s\n",
+ (buf[1] & 0x80) ? "high (inactive)" : "low (active)");
+
+ tuner_info("write: byte C 0x%02x\n", buf[2]);
+ tuner_info(" C0-4 top adjustment : %s dB\n",
+ adjust[buf[2] & 0x1f]);
+ tuner_info(" C5-6 de-emphasis : %s\n",
+ deemph[(buf[2] & 0x60) >> 5]);
+ tuner_info(" C7 audio gain : %s\n",
+ (buf[2] & 0x80) ? "-6" : "0");
+
+ tuner_info("write: byte E 0x%02x\n", buf[3]);
+ tuner_info(" E0-1 sound carrier : %s\n",
+ carrier[(buf[3] & 0x03)]);
+ tuner_info(" E6 l pll gating : %s\n",
+ (buf[3] & 0x40) ? "36" : "13");
if (buf[1] & 0x08) {
/* radio */
- tda9887_info(" E2-4 video if : %s\n",
- rif[(buf[3] & 0x0c) >> 2]);
- tda9887_info(" E7 vif agc output : %s\n",
- (buf[3] & 0x80)
- ? ((buf[3] & 0x10) ? "fm-agc radio" : "sif-agc radio")
- : "fm radio carrier afc");
+ tuner_info(" E2-4 video if : %s\n",
+ rif[(buf[3] & 0x0c) >> 2]);
+ tuner_info(" E7 vif agc output : %s\n",
+ (buf[3] & 0x80)
+ ? ((buf[3] & 0x10) ? "fm-agc radio" :
+ "sif-agc radio")
+ : "fm radio carrier afc");
} else {
/* video */
- tda9887_info(" E2-4 video if : %s\n",
- vif[(buf[3] & 0x1c) >> 2]);
- tda9887_info(" E5 tuner gain : %s\n",
- (buf[3] & 0x80)
- ? ((buf[3] & 0x20) ? "external" : "normal")
- : ((buf[3] & 0x20) ? "minimum" : "normal"));
- tda9887_info(" E7 vif agc output : %s\n",
- (buf[3] & 0x80)
- ? ((buf[3] & 0x20)
- ? "pin3 port, pin22 vif agc out"
- : "pin22 port, pin3 vif acg ext in")
- : "pin3+pin22 port");
+ tuner_info(" E2-4 video if : %s\n",
+ vif[(buf[3] & 0x1c) >> 2]);
+ tuner_info(" E5 tuner gain : %s\n",
+ (buf[3] & 0x80)
+ ? ((buf[3] & 0x20) ? "external" : "normal")
+ : ((buf[3] & 0x20) ? "minimum" : "normal"));
+ tuner_info(" E7 vif agc output : %s\n",
+ (buf[3] & 0x80) ? ((buf[3] & 0x20)
+ ? "pin3 port, pin22 vif agc out"
+ : "pin22 port, pin3 vif acg ext in")
+ : "pin3+pin22 port");
}
- tda9887_info("--\n");
+ tuner_info("--\n");
}
/* ---------------------------------------------------------------------- */
-static int tda9887_set_tvnorm(struct tuner *t, char *buf)
+static int tda9887_set_tvnorm(struct dvb_frontend *fe)
{
+ struct tda9887_priv *priv = fe->analog_demod_priv;
struct tvnorm *norm = NULL;
+ char *buf = priv->data;
int i;
- if (t->mode == V4L2_TUNER_RADIO) {
- if (t->audmode == V4L2_TUNER_MODE_MONO)
+ if (priv->mode == V4L2_TUNER_RADIO) {
+ if (priv->audmode == V4L2_TUNER_MODE_MONO)
norm = &radio_mono;
else
norm = &radio_stereo;
} else {
for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
- if (tvnorms[i].std & t->std) {
+ if (tvnorms[i].std & priv->std) {
norm = tvnorms+i;
break;
}
}
}
if (NULL == norm) {
- tda9887_dbg("Unsupported tvnorm entry - audio muted\n");
+ tuner_dbg("Unsupported tvnorm entry - audio muted\n");
return -1;
}
- tda9887_dbg("configure for: %s\n",norm->name);
+ tuner_dbg("configure for: %s\n", norm->name);
buf[1] = norm->b;
buf[2] = norm->c;
buf[3] = norm->e;
@@ -426,8 +437,11 @@ module_param(port2, int, 0644);
module_param(qss, int, 0644);
module_param(adjust, int, 0644);
-static int tda9887_set_insmod(struct tuner *t, char *buf)
+static int tda9887_set_insmod(struct dvb_frontend *fe)
{
+ struct tda9887_priv *priv = fe->analog_demod_priv;
+ char *buf = priv->data;
+
if (UNSET != port1) {
if (port1)
buf[1] |= cOutputPort1Inactive;
@@ -455,27 +469,30 @@ static int tda9887_set_insmod(struct tuner *t, char *buf)
return 0;
}
-static int tda9887_set_config(struct tuner *t, char *buf)
+static int tda9887_do_config(struct dvb_frontend *fe)
{
- if (t->tda9887_config & TDA9887_PORT1_ACTIVE)
+ struct tda9887_priv *priv = fe->analog_demod_priv;
+ char *buf = priv->data;
+
+ if (priv->config & TDA9887_PORT1_ACTIVE)
buf[1] &= ~cOutputPort1Inactive;
- if (t->tda9887_config & TDA9887_PORT1_INACTIVE)
+ if (priv->config & TDA9887_PORT1_INACTIVE)
buf[1] |= cOutputPort1Inactive;
- if (t->tda9887_config & TDA9887_PORT2_ACTIVE)
+ if (priv->config & TDA9887_PORT2_ACTIVE)
buf[1] &= ~cOutputPort2Inactive;
- if (t->tda9887_config & TDA9887_PORT2_INACTIVE)
+ if (priv->config & TDA9887_PORT2_INACTIVE)
buf[1] |= cOutputPort2Inactive;
- if (t->tda9887_config & TDA9887_QSS)
+ if (priv->config & TDA9887_QSS)
buf[1] |= cQSS;
- if (t->tda9887_config & TDA9887_INTERCARRIER)
+ if (priv->config & TDA9887_INTERCARRIER)
buf[1] &= ~cQSS;
- if (t->tda9887_config & TDA9887_AUTOMUTE)
+ if (priv->config & TDA9887_AUTOMUTE)
buf[1] |= cAutoMuteFmActive;
- if (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) {
+ if (priv->config & TDA9887_DEEMPHASIS_MASK) {
buf[2] &= ~0x60;
- switch (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) {
+ switch (priv->config & TDA9887_DEEMPHASIS_MASK) {
case TDA9887_DEEMPHASIS_NONE:
buf[2] |= cDeemphasisOFF;
break;
@@ -487,21 +504,22 @@ static int tda9887_set_config(struct tuner *t, char *buf)
break;
}
}
- if (t->tda9887_config & TDA9887_TOP_SET) {
+ if (priv->config & TDA9887_TOP_SET) {
buf[2] &= ~cTopMask;
- buf[2] |= (t->tda9887_config >> 8) & cTopMask;
+ buf[2] |= (priv->config >> 8) & cTopMask;
}
- if ((t->tda9887_config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC))
+ if ((priv->config & TDA9887_INTERCARRIER_NTSC) &&
+ (priv->std & V4L2_STD_NTSC))
buf[1] &= ~cQSS;
- if (t->tda9887_config & TDA9887_GATING_18)
+ if (priv->config & TDA9887_GATING_18)
buf[3] &= ~cGating_36;
- if (t->mode == V4L2_TUNER_RADIO) {
- if (t->tda9887_config & TDA9887_RIF_41_3) {
+ if (priv->mode == V4L2_TUNER_RADIO) {
+ if (priv->config & TDA9887_RIF_41_3) {
buf[3] &= ~cVideoIFMask;
buf[3] |= cRadioIF_41_30;
}
- if (t->tda9887_config & TDA9887_GAIN_NORMAL)
+ if (priv->config & TDA9887_GAIN_NORMAL)
buf[3] &= ~cTunerGainLow;
}
@@ -510,26 +528,26 @@ static int tda9887_set_config(struct tuner *t, char *buf)
/* ---------------------------------------------------------------------- */
-static int tda9887_status(struct tuner *t)
+static int tda9887_status(struct dvb_frontend *fe)
{
- struct tda9887_priv *priv = t->priv;
+ struct tda9887_priv *priv = fe->analog_demod_priv;
unsigned char buf[1];
int rc;
memset(buf,0,sizeof(buf));
if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,buf,1)))
- tda9887_info("i2c i/o error: rc == %d (should be 1)\n",rc);
- dump_read_message(t, buf);
+ tuner_info("i2c i/o error: rc == %d (should be 1)\n", rc);
+ dump_read_message(fe, buf);
return 0;
}
-static void tda9887_configure(struct tuner *t)
+static void tda9887_configure(struct dvb_frontend *fe)
{
- struct tda9887_priv *priv = t->priv;
+ struct tda9887_priv *priv = fe->analog_demod_priv;
int rc;
memset(priv->data,0,sizeof(priv->data));
- tda9887_set_tvnorm(t,priv->data);
+ tda9887_set_tvnorm(fe);
/* A note on the port settings:
These settings tend to depend on the specifics of the board.
@@ -547,38 +565,38 @@ static void tda9887_configure(struct tuner *t)
priv->data[1] |= cOutputPort1Inactive;
priv->data[1] |= cOutputPort2Inactive;
- tda9887_set_config(t,priv->data);
- tda9887_set_insmod(t,priv->data);
+ tda9887_do_config(fe);
+ tda9887_set_insmod(fe);
- if (t->mode == T_STANDBY) {
+ if (priv->mode == T_STANDBY)
priv->data[1] |= cForcedMuteAudioON;
- }
- tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
- priv->data[1],priv->data[2],priv->data[3]);
- if (tuner_debug > 1)
- dump_write_message(t, priv->data);
+ tuner_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
+ priv->data[1], priv->data[2], priv->data[3]);
+ if (debug > 1)
+ dump_write_message(fe, priv->data);
if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,priv->data,4)))
- tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc);
+ tuner_info("i2c i/o error: rc == %d (should be 4)\n", rc);
- if (tuner_debug > 2) {
+ if (debug > 2) {
msleep_interruptible(1000);
- tda9887_status(t);
+ tda9887_status(fe);
}
}
/* ---------------------------------------------------------------------- */
-static void tda9887_tuner_status(struct tuner *t)
+static void tda9887_tuner_status(struct dvb_frontend *fe)
{
- struct tda9887_priv *priv = t->priv;
- tda9887_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", priv->data[1], priv->data[2], priv->data[3]);
+ struct tda9887_priv *priv = fe->analog_demod_priv;
+ tuner_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n",
+ priv->data[1], priv->data[2], priv->data[3]);
}
-static int tda9887_get_afc(struct tuner *t)
+static int tda9887_get_afc(struct dvb_frontend *fe)
{
- struct tda9887_priv *priv = t->priv;
+ struct tda9887_priv *priv = fe->analog_demod_priv;
static int AFC_BITS_2_kHz[] = {
-12500, -37500, -62500, -97500,
-112500, -137500, -162500, -187500,
@@ -594,52 +612,79 @@ static int tda9887_get_afc(struct tuner *t)
return afc;
}
-static void tda9887_standby(struct tuner *t)
+static void tda9887_standby(struct dvb_frontend *fe)
+{
+ struct tda9887_priv *priv = fe->analog_demod_priv;
+
+ priv->mode = T_STANDBY;
+
+ tda9887_configure(fe);
+}
+
+static void tda9887_set_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
{
- tda9887_configure(t);
+ struct tda9887_priv *priv = fe->analog_demod_priv;
+
+ priv->mode = params->mode;
+ priv->audmode = params->audmode;
+ priv->std = params->std;
+ tda9887_configure(fe);
}
-static void tda9887_set_freq(struct tuner *t, unsigned int freq)
+static int tda9887_set_config(struct dvb_frontend *fe, void *priv_cfg)
{
- tda9887_configure(t);
+ struct tda9887_priv *priv = fe->analog_demod_priv;
+
+ priv->config = *(unsigned int *)priv_cfg;
+ tda9887_configure(fe);
+
+ return 0;
}
-static void tda9887_release(struct tuner *t)
+static void tda9887_release(struct dvb_frontend *fe)
{
- kfree(t->priv);
- t->priv = NULL;
+ kfree(fe->analog_demod_priv);
+ fe->analog_demod_priv = NULL;
}
-static struct tuner_operations tda9887_tuner_ops = {
- .set_tv_freq = tda9887_set_freq,
- .set_radio_freq = tda9887_set_freq,
+static struct analog_demod_ops tda9887_ops = {
+ .info = {
+ .name = "tda9887",
+ },
+ .set_params = tda9887_set_params,
.standby = tda9887_standby,
.tuner_status = tda9887_tuner_status,
.get_afc = tda9887_get_afc,
.release = tda9887_release,
+ .set_config = tda9887_set_config,
};
-int tda9887_tuner_init(struct tuner *t)
+struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c_adap,
+ u8 i2c_addr)
{
struct tda9887_priv *priv = NULL;
priv = kzalloc(sizeof(struct tda9887_priv), GFP_KERNEL);
if (priv == NULL)
- return -ENOMEM;
- t->priv = priv;
-
- priv->i2c_props.addr = t->i2c.addr;
- priv->i2c_props.adap = t->i2c.adapter;
+ return NULL;
+ fe->analog_demod_priv = priv;
- strlcpy(t->i2c.name, "tda9887", sizeof(t->i2c.name));
+ priv->i2c_props.addr = i2c_addr;
+ priv->i2c_props.adap = i2c_adap;
+ priv->mode = T_STANDBY;
- tda9887_info("tda988[5/6/7] found @ 0x%x (%s)\n", t->i2c.addr,
- t->i2c.driver->driver.name);
+ tuner_info("tda988[5/6/7] found\n");
- memcpy(&t->ops, &tda9887_tuner_ops, sizeof(struct tuner_operations));
+ memcpy(&fe->ops.analog_ops, &tda9887_ops,
+ sizeof(struct analog_demod_ops));
- return 0;
+ return fe;
}
+EXPORT_SYMBOL_GPL(tda9887_attach);
+
+MODULE_LICENSE("GPL");
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/drivers/media/video/tda9887.h b/drivers/media/video/tda9887.h
new file mode 100644
index 000000000000..8f873a8e6ed2
--- /dev/null
+++ b/drivers/media/video/tda9887.h
@@ -0,0 +1,38 @@
+/*
+ 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 __TDA9887_H__
+#define __TDA9887_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+/* ------------------------------------------------------------------------ */
+#if defined(CONFIG_TUNER_TDA9887) || (defined(CONFIG_TUNER_TDA9887_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c_adap,
+ u8 i2c_addr);
+#else
+static inline struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c_adap,
+ u8 i2c_addr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif
+
+#endif /* __TDA9887_H__ */
diff --git a/drivers/media/video/tea5761.c b/drivers/media/video/tea5761.c
index 2150222a3860..5326eeceaacd 100644
--- a/drivers/media/video/tea5761.c
+++ b/drivers/media/video/tea5761.c
@@ -18,7 +18,7 @@ static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");
-#define PREFIX "tea5761 "
+#define PREFIX "tea5761"
struct tea5761_priv {
struct tuner_i2c_props i2c_props;
diff --git a/drivers/media/video/tea5767.c b/drivers/media/video/tea5767.c
index 71df419df7bc..e1b48d87e7b7 100644
--- a/drivers/media/video/tea5767.c
+++ b/drivers/media/video/tea5767.c
@@ -20,12 +20,14 @@ static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");
-#define PREFIX "tea5767 "
+#define PREFIX "tea5767"
-struct tea5767_priv {
- struct tuner_i2c_props i2c_props;
+/*****************************************************************************/
- u32 frequency;
+struct tea5767_priv {
+ struct tuner_i2c_props i2c_props;
+ u32 frequency;
+ struct tea5767_ctrl ctrl;
};
/*****************************************************************************/
@@ -127,17 +129,10 @@ struct tea5767_priv {
/* Reserved for future extensions */
#define TEA5767_RESERVED_MASK 0xff
-enum tea5767_xtal_freq {
- TEA5767_LOW_LO_32768 = 0,
- TEA5767_HIGH_LO_32768 = 1,
- TEA5767_LOW_LO_13MHz = 2,
- TEA5767_HIGH_LO_13MHz = 3,
-};
-
-
/*****************************************************************************/
-static void tea5767_status_dump(unsigned char *buffer)
+static void tea5767_status_dump(struct tea5767_priv *priv,
+ unsigned char *buffer)
{
unsigned int div, frq;
@@ -153,7 +148,7 @@ static void tea5767_status_dump(unsigned char *buffer)
div = ((buffer[0] & 0x3f) << 8) | buffer[1];
- switch (TEA5767_HIGH_LO_32768) {
+ switch (priv->ctrl.xtal_freq) {
case TEA5767_HIGH_LO_13MHz:
frq = (div * 50000 - 700000 - 225000) / 4; /* Freq in KHz */
break;
@@ -202,13 +197,10 @@ static int set_radio_freq(struct dvb_frontend *fe,
tuner_dbg("radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000);
- /* Rounds freq to next decimal value - for 62.5 KHz step */
- /* frq = 20*(frq/16)+radio_frq[frq%16]; */
+ buffer[2] = 0;
- buffer[2] = TEA5767_PORT1_HIGH;
- buffer[3] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL |
- TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND;
- buffer[4] = 0;
+ if (priv->ctrl.port1)
+ buffer[2] |= TEA5767_PORT1_HIGH;
if (params->audmode == V4L2_TUNER_MODE_MONO) {
tuner_dbg("TEA5767 set to mono\n");
@@ -217,18 +209,45 @@ static int set_radio_freq(struct dvb_frontend *fe,
tuner_dbg("TEA5767 set to stereo\n");
}
- /* Should be replaced */
- switch (TEA5767_HIGH_LO_32768) {
+
+ buffer[3] = 0;
+
+ if (priv->ctrl.port2)
+ buffer[3] |= TEA5767_PORT2_HIGH;
+
+ if (priv->ctrl.high_cut)
+ buffer[3] |= TEA5767_HIGH_CUT_CTRL;
+
+ if (priv->ctrl.st_noise)
+ buffer[3] |= TEA5767_ST_NOISE_CTL;
+
+ if (priv->ctrl.soft_mute)
+ buffer[3] |= TEA5767_SOFT_MUTE;
+
+ if (priv->ctrl.japan_band)
+ buffer[3] |= TEA5767_JAPAN_BAND;
+
+ buffer[4] = 0;
+
+ if (priv->ctrl.deemph_75)
+ buffer[4] |= TEA5767_DEEMPH_75;
+
+ if (priv->ctrl.pllref)
+ buffer[4] |= TEA5767_PLLREF_ENABLE;
+
+
+ /* Rounds freq to next decimal value - for 62.5 KHz step */
+ /* frq = 20*(frq/16)+radio_frq[frq%16]; */
+
+ switch (priv->ctrl.xtal_freq) {
case TEA5767_HIGH_LO_13MHz:
tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n");
buffer[2] |= TEA5767_HIGH_LO_INJECT;
- buffer[4] |= TEA5767_PLLREF_ENABLE;
div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000;
break;
case TEA5767_LOW_LO_13MHz:
tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n");
- buffer[4] |= TEA5767_PLLREF_ENABLE;
div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000;
break;
case TEA5767_LOW_LO_32768:
@@ -256,7 +275,7 @@ static int set_radio_freq(struct dvb_frontend *fe,
if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5)))
tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
else
- tea5767_status_dump(buffer);
+ tea5767_status_dump(priv, buffer);
}
priv->frequency = frq * 125 / 2;
@@ -382,7 +401,6 @@ int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr)
return EINVAL;
}
- printk(KERN_WARNING "TEA5767 detected.\n");
return 0;
}
@@ -398,6 +416,16 @@ static int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct tea5767_priv *priv = fe->tuner_priv;
*frequency = priv->frequency;
+
+ return 0;
+}
+
+static int tea5767_set_config (struct dvb_frontend *fe, void *priv_cfg)
+{
+ struct tea5767_priv *priv = fe->tuner_priv;
+
+ memcpy(&priv->ctrl, priv_cfg, sizeof(priv->ctrl));
+
return 0;
}
@@ -407,6 +435,7 @@ static struct dvb_tuner_ops tea5767_tuner_ops = {
},
.set_analog_params = set_radio_freq,
+ .set_config = tea5767_set_config,
.sleep = tea5767_standby,
.release = tea5767_release,
.get_frequency = tea5767_get_frequency,
@@ -425,8 +454,14 @@ struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
return NULL;
fe->tuner_priv = priv;
- priv->i2c_props.addr = i2c_addr;
- priv->i2c_props.adap = i2c_adap;
+ priv->i2c_props.addr = i2c_addr;
+ priv->i2c_props.adap = i2c_adap;
+ priv->ctrl.xtal_freq = TEA5767_HIGH_LO_32768;
+ priv->ctrl.port1 = 1;
+ priv->ctrl.port2 = 1;
+ priv->ctrl.high_cut = 1;
+ priv->ctrl.st_noise = 1;
+ priv->ctrl.japan_band = 1;
memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops,
sizeof(struct dvb_tuner_ops));
@@ -436,7 +471,6 @@ struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
return fe;
}
-
EXPORT_SYMBOL_GPL(tea5767_attach);
EXPORT_SYMBOL_GPL(tea5767_autodetection);
diff --git a/drivers/media/video/tea5767.h b/drivers/media/video/tea5767.h
index 5d78281adcc2..a44451f61145 100644
--- a/drivers/media/video/tea5767.h
+++ b/drivers/media/video/tea5767.h
@@ -20,6 +20,25 @@
#include <linux/i2c.h>
#include "dvb_frontend.h"
+enum tea5767_xtal {
+ TEA5767_LOW_LO_32768 = 0,
+ TEA5767_HIGH_LO_32768 = 1,
+ TEA5767_LOW_LO_13MHz = 2,
+ TEA5767_HIGH_LO_13MHz = 3,
+};
+
+struct tea5767_ctrl {
+ unsigned int port1:1;
+ unsigned int port2:1;
+ unsigned int high_cut:1;
+ unsigned int st_noise:1;
+ unsigned int soft_mute:1;
+ unsigned int japan_band:1;
+ unsigned int deemph_75:1;
+ unsigned int pllref:1;
+ enum tea5767_xtal xtal_freq;
+};
+
#if defined(CONFIG_TUNER_TEA5767) || (defined(CONFIG_TUNER_TEA5767_MODULE) && defined(MODULE))
extern int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c
index 76b2e96429d9..dc7b9c220b90 100644
--- a/drivers/media/video/tlv320aic23b.c
+++ b/drivers/media/video/tlv320aic23b.c
@@ -31,6 +31,7 @@
#include <linux/i2c-id.h>
#include <linux/videodev.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-i2c-drv-legacy.h>
MODULE_DESCRIPTION("tlv320aic23b driver");
MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil");
@@ -56,37 +57,35 @@ static int tlv320aic23b_write(struct i2c_client *client, int reg, u16 val)
return -1;
}
- for (i = 0; i < 3; i++) {
- if (i2c_smbus_write_byte_data(client, (reg << 1) |
- (val >> 8), val & 0xff) == 0) {
+ for (i = 0; i < 3; i++)
+ if (i2c_smbus_write_byte_data(client,
+ (reg << 1) | (val >> 8), val & 0xff) == 0)
return 0;
- }
- }
v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
return -1;
}
-static int tlv320aic23b_command(struct i2c_client *client, unsigned int cmd,
- void *arg)
+static int tlv320aic23b_command(struct i2c_client *client,
+ unsigned int cmd, void *arg)
{
struct tlv320aic23b_state *state = i2c_get_clientdata(client);
struct v4l2_control *ctrl = arg;
- u32* freq = arg;
+ u32 *freq = arg;
switch (cmd) {
case VIDIOC_INT_AUDIO_CLOCK_FREQ:
switch (*freq) {
- case 32000: /* set sample rate to 32 kHz */
- tlv320aic23b_write(client, 8, 0x018);
- break;
- case 44100: /* set sample rate to 44.1 kHz */
- tlv320aic23b_write(client, 8, 0x022);
- break;
- case 48000: /* set sample rate to 48 kHz */
- tlv320aic23b_write(client, 8, 0x000);
- break;
- default:
- return -EINVAL;
+ case 32000: /* set sample rate to 32 kHz */
+ tlv320aic23b_write(client, 8, 0x018);
+ break;
+ case 44100: /* set sample rate to 44.1 kHz */
+ tlv320aic23b_write(client, 8, 0x022);
+ break;
+ case 48000: /* set sample rate to 48 kHz */
+ tlv320aic23b_write(client, 8, 0x000);
+ break;
+ default:
+ return -EINVAL;
}
break;
@@ -126,92 +125,53 @@ static int tlv320aic23b_command(struct i2c_client *client, unsigned int cmd,
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
-static struct i2c_driver i2c_driver;
-
-static int tlv320aic23b_attach(struct i2c_adapter *adapter, int address, int kind)
+static int tlv320aic23b_probe(struct i2c_client *client)
{
- struct i2c_client *client;
struct tlv320aic23b_state *state;
/* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return 0;
-
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == 0)
- return -ENOMEM;
-
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver;
- snprintf(client->name, sizeof(client->name) - 1, "tlv320aic23b");
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
- v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name);
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
state = kmalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL);
- if (state == NULL) {
- kfree(client);
+ if (state == NULL)
return -ENOMEM;
- }
state->muted = 0;
i2c_set_clientdata(client, state);
- /* initialize tlv320aic23b */
- tlv320aic23b_write(client, 15, 0x000); /* RESET */
- tlv320aic23b_write(client, 6, 0x00A); /* turn off DAC & mic input */
- tlv320aic23b_write(client, 7, 0x049); /* left-justified, 24-bit, master mode */
- tlv320aic23b_write(client, 0, 0x119); /* set gain on both channels to +3.0 dB */
- tlv320aic23b_write(client, 8, 0x000); /* set sample rate to 48 kHz */
- tlv320aic23b_write(client, 9, 0x001); /* activate digital interface */
-
- i2c_attach_client(client);
-
+ /* Initialize tlv320aic23b */
+
+ /* RESET */
+ tlv320aic23b_write(client, 15, 0x000);
+ /* turn off DAC & mic input */
+ tlv320aic23b_write(client, 6, 0x00A);
+ /* left-justified, 24-bit, master mode */
+ tlv320aic23b_write(client, 7, 0x049);
+ /* set gain on both channels to +3.0 dB */
+ tlv320aic23b_write(client, 0, 0x119);
+ /* set sample rate to 48 kHz */
+ tlv320aic23b_write(client, 8, 0x000);
+ /* activate digital interface */
+ tlv320aic23b_write(client, 9, 0x001);
return 0;
}
-static int tlv320aic23b_probe(struct i2c_adapter *adapter)
+static int tlv320aic23b_remove(struct i2c_client *client)
{
- if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, tlv320aic23b_attach);
- return 0;
-}
-
-static int tlv320aic23b_detach(struct i2c_client *client)
-{
- int err;
-
- err = i2c_detach_client(client);
- if (err) {
- return err;
- }
- kfree(client);
-
+ kfree(i2c_get_clientdata(client));
return 0;
}
/* ----------------------------------------------------------------------- */
-/* i2c implementation */
-static struct i2c_driver i2c_driver = {
- .driver = {
- .name = "tlv320aic23b",
- },
- .id = I2C_DRIVERID_TLV320AIC23B,
- .attach_adapter = tlv320aic23b_probe,
- .detach_client = tlv320aic23b_detach,
- .command = tlv320aic23b_command,
-};
-
-static int __init tlv320aic23b_init_module(void)
-{
- return i2c_add_driver(&i2c_driver);
-}
-
-static void __exit tlv320aic23b_cleanup_module(void)
-{
- i2c_del_driver(&i2c_driver);
-}
-
-module_init(tlv320aic23b_init_module);
-module_exit(tlv320aic23b_cleanup_module);
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tlv320aic23b",
+ .driverid = I2C_DRIVERID_TLV320AIC23B,
+ .command = tlv320aic23b_command,
+ .probe = tlv320aic23b_probe,
+ .remove = tlv320aic23b_remove,
+};
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 9e99f3636d3d..ba538f6fbcc3 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -19,15 +19,41 @@
#include <media/tuner.h>
#include <media/tuner-types.h>
#include <media/v4l2-common.h>
-#include "tuner-driver.h"
+#include <media/v4l2-i2c-drv-legacy.h>
#include "mt20xx.h"
#include "tda8290.h"
#include "tea5761.h"
#include "tea5767.h"
+#include "tuner-xc2028.h"
#include "tuner-simple.h"
+#include "tda9887.h"
+#include "xc5000.h"
#define UNSET (-1U)
+#define PREFIX t->i2c->driver->driver.name
+
+struct tuner {
+ /* device */
+ struct dvb_frontend fe;
+ struct i2c_client *i2c;
+ struct list_head list;
+ unsigned int using_v4l2:1;
+
+ /* keep track of the current settings */
+ v4l2_std_id std;
+ unsigned int tv_freq;
+ unsigned int radio_freq;
+ unsigned int audmode;
+
+ unsigned int mode;
+ unsigned int mode_mask; /* Combination of allowable modes */
+
+ unsigned int type; /* chip type id */
+ unsigned int config;
+ int (*tuner_callback) (void *dev, int command, int arg);
+};
+
/* standard i2c insmod options */
static unsigned short normal_i2c[] = {
#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
@@ -47,7 +73,34 @@ static unsigned int no_autodetect = 0;
static unsigned int show_i2c = 0;
/* insmod options used at runtime => read/write */
-int tuner_debug = 0;
+static int tuner_debug;
+
+#define tuner_warn(fmt, arg...) do { \
+ printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(t->i2c->adapter), \
+ t->i2c->addr, ##arg); \
+ } while (0)
+
+#define tuner_info(fmt, arg...) do { \
+ printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(t->i2c->adapter), \
+ t->i2c->addr, ##arg); \
+ } while (0)
+
+#define tuner_err(fmt, arg...) do { \
+ printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(t->i2c->adapter), \
+ t->i2c->addr, ##arg); \
+ } while (0)
+
+#define tuner_dbg(fmt, arg...) do { \
+ if (tuner_debug) \
+ printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(t->i2c->adapter), \
+ t->i2c->addr, ##arg); \
+ } while (0)
+
+/* ------------------------------------------------------------------------ */
static unsigned int tv_range[2] = { 44, 958 };
static unsigned int radio_range[2] = { 65, 108 };
@@ -71,66 +124,96 @@ MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
MODULE_LICENSE("GPL");
-static struct i2c_driver driver;
-static struct i2c_client client_template;
-
/* ---------------------------------------------------------------------- */
-static void fe_set_freq(struct tuner *t, unsigned int freq)
+static void fe_set_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
{
- struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
-
- struct analog_parameters params = {
- .frequency = freq,
- .mode = t->mode,
- .audmode = t->audmode,
- .std = t->std
- };
+ struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+ struct tuner *t = fe->analog_demod_priv;
if (NULL == fe_tuner_ops->set_analog_params) {
tuner_warn("Tuner frontend module has no way to set freq\n");
return;
}
- fe_tuner_ops->set_analog_params(&t->fe, &params);
+ fe_tuner_ops->set_analog_params(fe, params);
}
-static void fe_release(struct tuner *t)
+static void fe_release(struct dvb_frontend *fe)
{
- struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
-
- if (fe_tuner_ops->release)
- fe_tuner_ops->release(&t->fe);
+ if (fe->ops.tuner_ops.release)
+ fe->ops.tuner_ops.release(fe);
+
+ /* DO NOT kfree(fe->analog_demod_priv)
+ *
+ * If we are in this function, analog_demod_priv contains a pointer
+ * to struct tuner *t. This will be kfree'd in tuner_detach().
+ *
+ * Otherwise, fe->ops.analog_demod_ops->release will
+ * handle the cleanup for analog demodulator modules.
+ */
+ fe->analog_demod_priv = NULL;
}
-static void fe_standby(struct tuner *t)
+static void fe_standby(struct dvb_frontend *fe)
{
- struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+ struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
if (fe_tuner_ops->sleep)
- fe_tuner_ops->sleep(&t->fe);
+ fe_tuner_ops->sleep(fe);
}
-static int fe_has_signal(struct tuner *t)
+static int fe_has_signal(struct dvb_frontend *fe)
{
- struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
u16 strength = 0;
- if (fe_tuner_ops->get_rf_strength)
- fe_tuner_ops->get_rf_strength(&t->fe, &strength);
+ if (fe->ops.tuner_ops.get_rf_strength)
+ fe->ops.tuner_ops.get_rf_strength(fe, &strength);
return strength;
}
+static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+ struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+ struct tuner *t = fe->analog_demod_priv;
+
+ if (fe_tuner_ops->set_config)
+ return fe_tuner_ops->set_config(fe, priv_cfg);
+
+ tuner_warn("Tuner frontend module has no way to set config\n");
+
+ return 0;
+}
+
+static void tuner_status(struct dvb_frontend *fe);
+
+static struct analog_demod_ops tuner_core_ops = {
+ .set_params = fe_set_params,
+ .standby = fe_standby,
+ .release = fe_release,
+ .has_signal = fe_has_signal,
+ .set_config = fe_set_config,
+ .tuner_status = tuner_status
+};
+
/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
static void set_tv_freq(struct i2c_client *c, unsigned int freq)
{
struct tuner *t = i2c_get_clientdata(c);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ struct analog_parameters params = {
+ .mode = t->mode,
+ .audmode = t->audmode,
+ .std = t->std
+ };
if (t->type == UNSET) {
tuner_warn ("tuner type not set\n");
return;
}
- if (NULL == t->ops.set_tv_freq) {
+ if (NULL == analog_ops->set_params) {
tuner_warn ("Tuner has no way to set tv freq\n");
return;
}
@@ -145,18 +228,27 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq)
else
freq = tv_range[1] * 16;
}
- t->ops.set_tv_freq(t, freq);
+ params.frequency = freq;
+
+ analog_ops->set_params(&t->fe, &params);
}
static void set_radio_freq(struct i2c_client *c, unsigned int freq)
{
struct tuner *t = i2c_get_clientdata(c);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ struct analog_parameters params = {
+ .mode = t->mode,
+ .audmode = t->audmode,
+ .std = t->std
+ };
if (t->type == UNSET) {
tuner_warn ("tuner type not set\n");
return;
}
- if (NULL == t->ops.set_radio_freq) {
+ if (NULL == analog_ops->set_params) {
tuner_warn ("tuner has no way to set radio frequency\n");
return;
}
@@ -171,8 +263,9 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq)
else
freq = radio_range[1] * 16000;
}
+ params.frequency = freq;
- t->ops.set_radio_freq(t, freq);
+ analog_ops->set_params(&t->fe, &params);
}
static void set_freq(struct i2c_client *c, unsigned long freq)
@@ -193,54 +286,65 @@ static void set_freq(struct i2c_client *c, unsigned long freq)
set_tv_freq(c, freq);
t->tv_freq = freq;
break;
+ default:
+ tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode);
}
}
static void tuner_i2c_address_check(struct tuner *t)
{
if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
- ((t->i2c.addr < 0x64) || (t->i2c.addr > 0x6f)))
+ ((t->i2c->addr < 0x64) || (t->i2c->addr > 0x6f)))
+ return;
+
+ /* We already know that the XC5000 can only be located at
+ * i2c address 0x61, 0x62, 0x63 or 0x64 */
+ if ((t->type == TUNER_XC5000) &&
+ ((t->i2c->addr <= 0x64)) && (t->i2c->addr >= 0x61))
return;
tuner_warn("====================== WARNING! ======================\n");
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->i2c->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->adapter->name, t->i2c->addr, t->type,
tuners[t->type].name);
tuner_warn("====================== WARNING! ======================\n");
}
-static void attach_tda8290(struct tuner *t)
-{
- struct tda8290_config cfg = {
- .lna_cfg = &t->config,
- .tuner_callback = t->tuner_callback
- };
- tda8290_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
-}
-
static void attach_simple_tuner(struct tuner *t)
{
struct simple_tuner_config cfg = {
.type = t->type,
.tun = &tuners[t->type]
};
- simple_tuner_attach(&t->fe, t->i2c.adapter, t->i2c.addr, &cfg);
+ simple_tuner_attach(&t->fe, t->i2c->adapter, t->i2c->addr, &cfg);
}
+static void attach_tda829x(struct tuner *t)
+{
+ struct tda829x_config cfg = {
+ .lna_cfg = &t->config,
+ .tuner_callback = t->tuner_callback,
+ };
+ tda829x_attach(&t->fe, t->i2c->adapter, t->i2c->addr, &cfg);
+}
+
+static struct xc5000_config xc5000_cfg;
+
static void set_type(struct i2c_client *c, unsigned int type,
unsigned int new_mode_mask, unsigned int new_config,
int (*tuner_callback) (void *dev, int command,int arg))
{
struct tuner *t = i2c_get_clientdata(c);
struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
unsigned char buffer[4];
if (type == UNSET || type == TUNER_ABSENT) {
@@ -260,32 +364,27 @@ static void set_type(struct i2c_client *c, unsigned int type,
t->tuner_callback = tuner_callback;
}
- /* This code detects calls by card attach_inform */
- if (NULL == t->i2c.dev.driver) {
+ if (t->mode == T_UNINITIALIZED) {
tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr);
return;
}
/* discard private data, in case set_type() was previously called */
- if (t->ops.release)
- t->ops.release(t);
- else {
- kfree(t->priv);
- t->priv = NULL;
- }
+ if (analog_ops->release)
+ analog_ops->release(&t->fe);
switch (t->type) {
case TUNER_MT2032:
- microtune_attach(&t->fe, t->i2c.adapter, t->i2c.addr);
+ microtune_attach(&t->fe, t->i2c->adapter, t->i2c->addr);
break;
case TUNER_PHILIPS_TDA8290:
{
- attach_tda8290(t);
+ attach_tda829x(t);
break;
}
case TUNER_TEA5767:
- if (tea5767_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
+ if (tea5767_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) {
t->type = TUNER_ABSENT;
t->mode_mask = T_UNINITIALIZED;
return;
@@ -293,7 +392,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
t->mode_mask = T_RADIO;
break;
case TUNER_TEA5761:
- if (tea5761_attach(&t->fe, t->i2c.adapter, t->i2c.addr) == NULL) {
+ if (tea5761_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) {
t->type = TUNER_ABSENT;
t->mode_mask = T_UNINITIALIZED;
return;
@@ -320,25 +419,60 @@ static void set_type(struct i2c_client *c, unsigned int type,
i2c_master_send(c,buffer,4);
attach_simple_tuner(t);
break;
+ case TUNER_XC2028:
+ {
+ struct xc2028_config cfg = {
+ .i2c_adap = t->i2c->adapter,
+ .i2c_addr = t->i2c->addr,
+ .video_dev = c->adapter->algo_data,
+ .callback = t->tuner_callback,
+ };
+ if (!xc2028_attach(&t->fe, &cfg)) {
+ t->type = TUNER_ABSENT;
+ t->mode_mask = T_UNINITIALIZED;
+ return;
+ }
+ break;
+ }
case TUNER_TDA9887:
- tda9887_tuner_init(t);
+ tda9887_attach(&t->fe, t->i2c->adapter, t->i2c->addr);
+ break;
+ case TUNER_XC5000:
+ xc5000_cfg.i2c_address = t->i2c->addr;
+ xc5000_cfg.if_khz = 5380;
+ xc5000_cfg.priv = c->adapter->algo_data;
+ xc5000_cfg.tuner_callback = t->tuner_callback;
+ if (!xc5000_attach(&t->fe, t->i2c->adapter, &xc5000_cfg)) {
+ t->type = TUNER_ABSENT;
+ t->mode_mask = T_UNINITIALIZED;
+ return;
+ }
+ {
+ struct dvb_tuner_ops *xc_tuner_ops;
+ xc_tuner_ops = &t->fe.ops.tuner_ops;
+ if(xc_tuner_ops->init != NULL)
+ xc_tuner_ops->init(&t->fe);
+ }
break;
default:
attach_simple_tuner(t);
break;
}
- if (fe_tuner_ops->set_analog_params) {
- strlcpy(t->i2c.name, fe_tuner_ops->info.name, sizeof(t->i2c.name));
+ 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->ops.set_tv_freq = fe_set_freq;
- t->ops.set_radio_freq = fe_set_freq;
- t->ops.standby = fe_standby;
- t->ops.release = fe_release;
- t->ops.has_signal = fe_has_signal;
+ 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));
}
- tuner_info("type set to %s\n", t->i2c.name);
+ tuner_dbg("type set to %s\n", t->i2c->name);
if (t->mode_mask == T_UNINITIALIZED)
t->mode_mask = new_mode_mask;
@@ -508,10 +642,12 @@ static int tuner_fixup_std(struct tuner *t)
return 0;
}
-static void tuner_status(struct tuner *t)
+static void tuner_status(struct dvb_frontend *fe)
{
+ struct tuner *t = fe->analog_demod_priv;
unsigned long freq, freq_fraction;
struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
const char *p;
switch (t->mode) {
@@ -541,172 +677,16 @@ static void tuner_status(struct tuner *t)
if (tuner_status & TUNER_STATUS_STEREO)
tuner_info("Stereo: yes\n");
}
- if (t->ops.has_signal) {
- tuner_info("Signal strength: %d\n", t->ops.has_signal(t));
- }
- if (t->ops.is_stereo) {
- tuner_info("Stereo: %s\n", t->ops.is_stereo(t) ? "yes" : "no");
- }
+ if (analog_ops->has_signal)
+ tuner_info("Signal strength: %d\n",
+ analog_ops->has_signal(fe));
+ if (analog_ops->is_stereo)
+ tuner_info("Stereo: %s\n",
+ analog_ops->is_stereo(fe) ? "yes" : "no");
}
/* ---------------------------------------------------------------------- */
-/* static vars: used only in tuner_attach and tuner_probe */
-static unsigned default_mode_mask;
-
-/* During client attach, set_type is called by adapter's attach_inform callback.
- set_type must then be completed by tuner_attach.
- */
-static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
-{
- struct tuner *t;
-
- client_template.adapter = adap;
- client_template.addr = addr;
-
- t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
- if (NULL == t)
- return -ENOMEM;
- memcpy(&t->i2c, &client_template, sizeof(struct i2c_client));
- i2c_set_clientdata(&t->i2c, t);
- t->type = UNSET;
- t->audmode = V4L2_TUNER_MODE_STEREO;
- t->mode_mask = T_UNINITIALIZED;
- t->ops.tuner_status = tuner_status;
-
- if (show_i2c) {
- unsigned char buffer[16];
- int i,rc;
-
- memset(buffer, 0, sizeof(buffer));
- rc = i2c_master_recv(&t->i2c, buffer, sizeof(buffer));
- tuner_info("I2C RECV = ");
- for (i=0;i<rc;i++)
- printk("%02x ",buffer[i]);
- printk("\n");
- }
- /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
- if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
- return -ENODEV;
-
- /* autodetection code based on the i2c addr */
- if (!no_autodetect) {
- switch (addr) {
- case 0x10:
- if (tea5761_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
- t->type = TUNER_TEA5761;
- t->mode_mask = T_RADIO;
- t->mode = T_STANDBY;
- t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
- default_mode_mask &= ~T_RADIO;
-
- goto register_client;
- }
- break;
- case 0x42:
- case 0x43:
- case 0x4a:
- case 0x4b:
- /* If chip is not tda8290, don't register.
- since it can be tda9887*/
- if (tda8290_probe(t->i2c.adapter, t->i2c.addr) == 0) {
- tuner_dbg("chip at addr %x is a tda8290\n", addr);
- } else {
- /* Default is being tda9887 */
- t->type = TUNER_TDA9887;
- t->mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
- t->mode = T_STANDBY;
- goto register_client;
- }
- break;
- case 0x60:
- if (tea5767_autodetection(t->i2c.adapter, t->i2c.addr) != EINVAL) {
- t->type = TUNER_TEA5767;
- t->mode_mask = T_RADIO;
- t->mode = T_STANDBY;
- t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
- default_mode_mask &= ~T_RADIO;
-
- goto register_client;
- }
- break;
- }
- }
-
- /* Initializes only the first adapter found */
- if (default_mode_mask != T_UNINITIALIZED) {
- tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask);
- t->mode_mask = default_mode_mask;
- t->tv_freq = 400 * 16; /* Sets freq to VHF High */
- t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
- default_mode_mask = T_UNINITIALIZED;
- }
-
- /* Should be just before return */
-register_client:
- tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name);
- i2c_attach_client (&t->i2c);
- set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback);
- return 0;
-}
-
-static int tuner_probe(struct i2c_adapter *adap)
-{
- if (0 != addr) {
- normal_i2c[0] = addr;
- normal_i2c[1] = I2C_CLIENT_END;
- }
-
- /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
- * FusionHDTV5 RT Gold has an ir receiver at 0x6b
- * and an RTC at 0x6f which can get corrupted if probed.
- */
- if ((adap->id == I2C_HW_B_CX2388x) ||
- (adap->id == I2C_HW_B_CX23885)) {
- unsigned int i = 0;
-
- while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
- i += 2;
- if (i + 4 < I2C_CLIENT_MAX_OPTS) {
- ignore[i+0] = adap->nr;
- ignore[i+1] = 0x6b;
- ignore[i+2] = adap->nr;
- ignore[i+3] = 0x6f;
- ignore[i+4] = I2C_CLIENT_END;
- } else
- printk(KERN_WARNING "tuner: "
- "too many options specified "
- "in i2c probe ignore list!\n");
- }
-
- default_mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV;
-
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, tuner_attach);
- return 0;
-}
-
-static int tuner_detach(struct i2c_client *client)
-{
- struct tuner *t = i2c_get_clientdata(client);
- int err;
-
- err = i2c_detach_client(&t->i2c);
- if (err) {
- tuner_warn
- ("Client deregistration failed, client not detached.\n");
- return err;
- }
-
- if (t->ops.release)
- t->ops.release(t);
- else {
- kfree(t->priv);
- }
- kfree(t);
- return 0;
-}
-
/*
* Switch tuner to other mode. If tuner support both tv and radio,
* set another frequency to some value (This is needed for some pal
@@ -716,6 +696,8 @@ static int tuner_detach(struct i2c_client *client)
static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
{
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
if (mode == t->mode)
return 0;
@@ -723,8 +705,8 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode,
if (check_mode(t, cmd) == EINVAL) {
t->mode = T_STANDBY;
- if (t->ops.standby)
- t->ops.standby(t);
+ if (analog_ops->standby)
+ analog_ops->standby(&t->fe);
return EINVAL;
}
return 0;
@@ -747,9 +729,10 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
struct tuner *t = i2c_get_clientdata(client);
struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
if (tuner_debug>1)
- v4l_i2c_print_ioctl(&(t->i2c),cmd);
+ v4l_i2c_print_ioctl(client,cmd);
switch (cmd) {
/* --- configuration --- */
@@ -773,8 +756,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
return 0;
t->mode = T_STANDBY;
- if (t->ops.standby)
- t->ops.standby(t);
+ if (analog_ops->standby)
+ analog_ops->standby(&t->fe);
break;
#ifdef CONFIG_VIDEO_V4L1
case VIDIOCSAUDIO:
@@ -842,8 +825,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
else
vt->flags &= ~VIDEO_TUNER_STEREO_ON;
} else {
- if (t->ops.is_stereo) {
- if (t->ops.is_stereo(t))
+ if (analog_ops->is_stereo) {
+ if (analog_ops->is_stereo(&t->fe))
vt->flags |=
VIDEO_TUNER_STEREO_ON;
else
@@ -851,8 +834,9 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
~VIDEO_TUNER_STEREO_ON;
}
}
- if (t->ops.has_signal)
- vt->signal = t->ops.has_signal(t);
+ if (analog_ops->has_signal)
+ vt->signal =
+ analog_ops->has_signal(&t->fe);
vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
@@ -882,21 +866,28 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
fe_tuner_ops->get_status(&t->fe, &tuner_status);
va->mode = (tuner_status & TUNER_STATUS_STEREO)
? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
- } else if (t->ops.is_stereo)
- va->mode = t->ops.is_stereo(t)
+ } else if (analog_ops->is_stereo)
+ va->mode = analog_ops->is_stereo(&t->fe)
? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
}
return 0;
}
#endif
- case TDA9887_SET_CONFIG:
- if (t->type == TUNER_TDA9887) {
- int *i = arg;
+ case TUNER_SET_CONFIG:
+ {
+ struct v4l2_priv_tun_config *cfg = arg;
- t->tda9887_config = *i;
- set_freq(client, t->tv_freq);
+ if (t->type != cfg->tuner)
+ break;
+
+ if (analog_ops->set_config) {
+ analog_ops->set_config(&t->fe, cfg->priv);
+ break;
}
+
+ tuner_dbg("Tuner frontend module has no way to set config\n");
break;
+ }
/* --- v4l ioctls --- */
/* take care: bttv does userspace copying, we'll get a
kernel pointer here... */
@@ -958,8 +949,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
switch_v4l2();
tuner->type = t->mode;
- if (t->ops.get_afc)
- tuner->afc=t->ops.get_afc(t);
+ if (analog_ops->get_afc)
+ tuner->afc = analog_ops->get_afc(&t->fe);
if (t->mode == V4L2_TUNER_ANALOG_TV)
tuner->capability |= V4L2_TUNER_CAP_NORM;
if (t->mode != V4L2_TUNER_RADIO) {
@@ -975,16 +966,20 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
u32 tuner_status;
fe_tuner_ops->get_status(&t->fe, &tuner_status);
- tuner->rxsubchans = (tuner_status & TUNER_STATUS_STEREO) ?
- V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
+ tuner->rxsubchans =
+ (tuner_status & TUNER_STATUS_STEREO) ?
+ V4L2_TUNER_SUB_STEREO :
+ V4L2_TUNER_SUB_MONO;
} else {
- if (t->ops.is_stereo) {
- tuner->rxsubchans = t->ops.is_stereo(t) ?
- V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
+ if (analog_ops->is_stereo) {
+ tuner->rxsubchans =
+ analog_ops->is_stereo(&t->fe) ?
+ V4L2_TUNER_SUB_STEREO :
+ V4L2_TUNER_SUB_MONO;
}
}
- if (t->ops.has_signal)
- tuner->signal = t->ops.has_signal(t);
+ if (analog_ops->has_signal)
+ tuner->signal = analog_ops->has_signal(&t->fe);
tuner->capability |=
V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
tuner->audmode = t->audmode;
@@ -1009,8 +1004,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
break;
}
case VIDIOC_LOG_STATUS:
- if (t->ops.tuner_status)
- t->ops.tuner_status(t);
+ if (analog_ops->tuner_status)
+ analog_ops->tuner_status(&t->fe);
break;
}
@@ -1019,18 +1014,18 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
static int tuner_suspend(struct i2c_client *c, pm_message_t state)
{
- struct tuner *t = i2c_get_clientdata (c);
+ struct tuner *t = i2c_get_clientdata(c);
- tuner_dbg ("suspend\n");
+ tuner_dbg("suspend\n");
/* FIXME: power down ??? */
return 0;
}
static int tuner_resume(struct i2c_client *c)
{
- struct tuner *t = i2c_get_clientdata (c);
+ struct tuner *t = i2c_get_clientdata(c);
- tuner_dbg ("resume\n");
+ tuner_dbg("resume\n");
if (V4L2_TUNER_RADIO == t->mode) {
if (t->radio_freq)
set_freq(c, t->radio_freq);
@@ -1041,36 +1036,227 @@ static int tuner_resume(struct i2c_client *c)
return 0;
}
-/* ----------------------------------------------------------------------- */
+/* ---------------------------------------------------------------------- */
-static struct i2c_driver driver = {
- .id = I2C_DRIVERID_TUNER,
- .attach_adapter = tuner_probe,
- .detach_client = tuner_detach,
- .command = tuner_command,
- .suspend = tuner_suspend,
- .resume = tuner_resume,
- .driver = {
- .name = "tuner",
- },
-};
-static struct i2c_client client_template = {
- .name = "(tuner unset)",
- .driver = &driver,
-};
+LIST_HEAD(tuner_list);
-static int __init tuner_init_module(void)
+/* Search for existing radio and/or TV tuners on the given I2C adapter.
+ Note that when this function is called from tuner_probe you can be
+ certain no other devices will be added/deleted at the same time, I2C
+ core protects against that. */
+static void tuner_lookup(struct i2c_adapter *adap,
+ struct tuner **radio, struct tuner **tv)
{
- return i2c_add_driver(&driver);
+ struct tuner *pos;
+
+ *radio = NULL;
+ *tv = NULL;
+
+ list_for_each_entry(pos, &tuner_list, list) {
+ int mode_mask;
+
+ if (pos->i2c->adapter != adap ||
+ pos->i2c->driver->id != I2C_DRIVERID_TUNER)
+ continue;
+
+ mode_mask = pos->mode_mask & ~T_STANDBY;
+ if (*radio == NULL && mode_mask == T_RADIO)
+ *radio = pos;
+ /* Note: currently TDA9887 is the only demod-only
+ device. If other devices appear then we need to
+ make this test more general. */
+ else if (*tv == NULL && pos->type != TUNER_TDA9887 &&
+ (pos->mode_mask & (T_ANALOG_TV | T_DIGITAL_TV)))
+ *tv = pos;
+ }
}
-static void __exit tuner_cleanup_module(void)
+/* During client attach, set_type is called by adapter's attach_inform callback.
+ set_type must then be completed by tuner_probe.
+ */
+static int tuner_probe(struct i2c_client *client)
{
- i2c_del_driver(&driver);
+ struct tuner *t;
+ struct tuner *radio;
+ struct tuner *tv;
+
+ t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
+ if (NULL == t)
+ return -ENOMEM;
+ t->i2c = client;
+ strlcpy(client->name, "(tuner unset)", sizeof(client->name));
+ i2c_set_clientdata(client, t);
+ t->type = UNSET;
+ t->audmode = V4L2_TUNER_MODE_STEREO;
+ t->mode_mask = T_UNINITIALIZED;
+
+ if (show_i2c) {
+ unsigned char buffer[16];
+ int i, rc;
+
+ memset(buffer, 0, sizeof(buffer));
+ rc = i2c_master_recv(client, buffer, sizeof(buffer));
+ tuner_info("I2C RECV = ");
+ for (i = 0; i < rc; i++)
+ printk(KERN_CONT "%02x ", buffer[i]);
+ printk("\n");
+ }
+ /* HACK: This test was added to avoid tuner to probe tda9840 and
+ tea6415c on the MXB card */
+ if (client->adapter->id == I2C_HW_SAA7146 && client->addr < 0x4a) {
+ kfree(t);
+ return -ENODEV;
+ }
+
+ /* autodetection code based on the i2c addr */
+ if (!no_autodetect) {
+ switch (client->addr) {
+ case 0x10:
+ if (tea5761_autodetection(t->i2c->adapter, t->i2c->addr)
+ != EINVAL) {
+ t->type = TUNER_TEA5761;
+ t->mode_mask = T_RADIO;
+ t->mode = T_STANDBY;
+ /* Sets freq to FM range */
+ t->radio_freq = 87.5 * 16000;
+ tuner_lookup(t->i2c->adapter, &radio, &tv);
+ if (tv)
+ tv->mode_mask &= ~T_RADIO;
+
+ goto register_client;
+ }
+ break;
+ case 0x42:
+ case 0x43:
+ case 0x4a:
+ case 0x4b:
+ /* If chip is not tda8290, don't register.
+ since it can be tda9887*/
+ if (tda829x_probe(t->i2c->adapter,
+ t->i2c->addr) == 0) {
+ tuner_dbg("tda829x detected\n");
+ } else {
+ /* Default is being tda9887 */
+ t->type = TUNER_TDA9887;
+ t->mode_mask = T_RADIO | T_ANALOG_TV |
+ T_DIGITAL_TV;
+ t->mode = T_STANDBY;
+ goto register_client;
+ }
+ break;
+ case 0x60:
+ if (tea5767_autodetection(t->i2c->adapter, t->i2c->addr)
+ != EINVAL) {
+ t->type = TUNER_TEA5767;
+ t->mode_mask = T_RADIO;
+ t->mode = T_STANDBY;
+ /* Sets freq to FM range */
+ t->radio_freq = 87.5 * 16000;
+ tuner_lookup(t->i2c->adapter, &radio, &tv);
+ if (tv)
+ tv->mode_mask &= ~T_RADIO;
+
+ goto register_client;
+ }
+ break;
+ }
+ }
+
+ /* Initializes only the first TV tuner on this adapter. Why only the
+ first? Because there are some devices (notably the ones with TI
+ tuners) that have more than one i2c address for the *same* device.
+ Experience shows that, except for just one case, the first
+ address is the right one. The exception is a Russian tuner
+ (ACORP_Y878F). So, the desired behavior is just to enable the
+ first found TV tuner. */
+ tuner_lookup(t->i2c->adapter, &radio, &tv);
+ if (tv == NULL) {
+ t->mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
+ if (radio == NULL)
+ t->mode_mask |= T_RADIO;
+ tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask);
+ t->tv_freq = 400 * 16; /* Sets freq to VHF High */
+ t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */
+ }
+
+ /* Should be just before return */
+register_client:
+ tuner_info("chip found @ 0x%x (%s)\n", client->addr << 1,
+ client->adapter->name);
+
+ /* Sets a default mode */
+ if (t->mode_mask & T_ANALOG_TV) {
+ t->mode = V4L2_TUNER_ANALOG_TV;
+ } else if (t->mode_mask & T_RADIO) {
+ t->mode = V4L2_TUNER_RADIO;
+ } else {
+ t->mode = V4L2_TUNER_DIGITAL_TV;
+ }
+ set_type(client, t->type, t->mode_mask, t->config, t->tuner_callback);
+ list_add_tail(&t->list, &tuner_list);
+ return 0;
+}
+
+static int tuner_legacy_probe(struct i2c_adapter *adap)
+{
+ if (0 != addr) {
+ normal_i2c[0] = addr;
+ normal_i2c[1] = I2C_CLIENT_END;
+ }
+
+ if ((adap->class & I2C_CLASS_TV_ANALOG) == 0)
+ return 0;
+
+ /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
+ * FusionHDTV5 RT Gold has an ir receiver at 0x6b
+ * and an RTC at 0x6f which can get corrupted if probed.
+ */
+ if ((adap->id == I2C_HW_B_CX2388x) ||
+ (adap->id == I2C_HW_B_CX23885)) {
+ unsigned int i = 0;
+
+ while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
+ i += 2;
+ if (i + 4 < I2C_CLIENT_MAX_OPTS) {
+ ignore[i+0] = adap->nr;
+ ignore[i+1] = 0x6b;
+ ignore[i+2] = adap->nr;
+ ignore[i+3] = 0x6f;
+ ignore[i+4] = I2C_CLIENT_END;
+ } else
+ printk(KERN_WARNING "tuner: "
+ "too many options specified "
+ "in i2c probe ignore list!\n");
+ }
+ return 1;
}
-module_init(tuner_init_module);
-module_exit(tuner_cleanup_module);
+static int tuner_remove(struct i2c_client *client)
+{
+ struct tuner *t = i2c_get_clientdata(client);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ if (analog_ops->release)
+ analog_ops->release(&t->fe);
+
+ list_del(&t->list);
+ kfree(t);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tuner",
+ .driverid = I2C_DRIVERID_TUNER,
+ .command = tuner_command,
+ .probe = tuner_probe,
+ .remove = tuner_remove,
+ .suspend = tuner_suspend,
+ .resume = tuner_resume,
+ .legacy_probe = tuner_legacy_probe,
+};
+
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/drivers/media/video/tuner-driver.h b/drivers/media/video/tuner-driver.h
deleted file mode 100644
index 28a10da76d12..000000000000
--- a/drivers/media/video/tuner-driver.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- tuner-driver.h - interface for different tuners
-
- Copyright (C) 1997 Markus Schroeder (schroedm@uni-duesseldorf.de)
- minor modifications by Ralph Metzler (rjkm@thp.uni-koeln.de)
-
- 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 __TUNER_DRIVER_H__
-#define __TUNER_DRIVER_H__
-
-#include <linux/videodev2.h>
-#include <linux/i2c.h>
-#include "tuner-i2c.h"
-#include "dvb_frontend.h"
-
-extern unsigned const int tuner_count;
-
-struct tuner;
-
-struct tuner_operations {
- void (*set_tv_freq)(struct tuner *t, unsigned int freq);
- void (*set_radio_freq)(struct tuner *t, unsigned int freq);
- int (*has_signal)(struct tuner *t);
- int (*is_stereo)(struct tuner *t);
- int (*get_afc)(struct tuner *t);
- void (*tuner_status)(struct tuner *t);
- void (*standby)(struct tuner *t);
- void (*release)(struct tuner *t);
-};
-
-struct tuner {
- /* device */
- struct i2c_client i2c;
-
- unsigned int type; /* chip type */
-
- unsigned int mode;
- unsigned int mode_mask; /* Combination of allowable modes */
-
- unsigned int tv_freq; /* keep track of the current settings */
- unsigned int radio_freq;
- unsigned int audmode;
- v4l2_std_id std;
-
- int using_v4l2;
- void *priv;
-
- struct dvb_frontend fe;
-
- /* used by tda9887 */
- unsigned int tda9887_config;
-
- unsigned int config;
- int (*tuner_callback) (void *dev, int command,int arg);
-
- struct tuner_operations ops;
-};
-
-/* ------------------------------------------------------------------------ */
-
-extern int tda9887_tuner_init(struct tuner *t);
-
-/* ------------------------------------------------------------------------ */
-
-#define tuner_warn(fmt, arg...) do {\
- printk(KERN_WARNING "%s %d-%04x: " fmt, t->i2c.driver->driver.name, \
- i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
-#define tuner_info(fmt, arg...) do {\
- printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.driver->driver.name, \
- i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
-#define tuner_dbg(fmt, arg...) do {\
- extern int tuner_debug; \
- if (tuner_debug) \
- printk(KERN_DEBUG "%s %d-%04x: " fmt, t->i2c.driver->driver.name, \
- i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
-
-#endif /* __TUNER_DRIVER_H__ */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/tuner-i2c.h b/drivers/media/video/tuner-i2c.h
index 159019ec3373..de52e8ffd347 100644
--- a/drivers/media/video/tuner-i2c.h
+++ b/drivers/media/video/tuner-i2c.h
@@ -46,25 +46,42 @@ static inline int tuner_i2c_xfer_recv(struct tuner_i2c_props *props, char *buf,
return (ret == 1) ? len : ret;
}
-#ifndef __TUNER_DRIVER_H__
-#define tuner_warn(fmt, arg...) do {\
- printk(KERN_WARNING PREFIX "%d-%04x: " fmt, \
- i2c_adapter_id(priv->i2c_props.adap), priv->i2c_props.addr , ##arg); } while (0)
-#define tuner_info(fmt, arg...) do {\
- printk(KERN_INFO PREFIX "%d-%04x: " fmt, \
- i2c_adapter_id(priv->i2c_props.adap), priv->i2c_props.addr , ##arg); } while (0)
-#define tuner_dbg(fmt, arg...) do {\
- if ((debug)) \
- printk(KERN_DEBUG PREFIX "%d-%04x: " fmt, \
- i2c_adapter_id(priv->i2c_props.adap), priv->i2c_props.addr , ##arg); } while (0)
-#endif /* __TUNER_DRIVER_H__ */
+static inline int tuner_i2c_xfer_send_recv(struct tuner_i2c_props *props,
+ char *obuf, int olen,
+ char *ibuf, int ilen)
+{
+ struct i2c_msg msg[2] = { { .addr = props->addr, .flags = 0,
+ .buf = obuf, .len = olen },
+ { .addr = props->addr, .flags = I2C_M_RD,
+ .buf = ibuf, .len = ilen } };
+ int ret = i2c_transfer(props->adap, msg, 2);
-#endif /* __TUNER_I2C_H__ */
+ return (ret == 2) ? ilen : ret;
+}
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+#define tuner_warn(fmt, arg...) do { \
+ printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(priv->i2c_props.adap), \
+ priv->i2c_props.addr, ##arg); \
+ } while (0)
+
+#define tuner_info(fmt, arg...) do { \
+ printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(priv->i2c_props.adap), \
+ priv->i2c_props.addr , ##arg); \
+ } while (0)
+
+#define tuner_err(fmt, arg...) do { \
+ printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(priv->i2c_props.adap), \
+ priv->i2c_props.addr , ##arg); \
+ } while (0)
+
+#define tuner_dbg(fmt, arg...) do { \
+ if ((debug)) \
+ printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(priv->i2c_props.adap), \
+ priv->i2c_props.addr , ##arg); \
+ } while (0)
+
+#endif /* __TUNER_I2C_H__ */
diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c
index 7b93d3b1f4c6..c1db576696c6 100644
--- a/drivers/media/video/tuner-simple.c
+++ b/drivers/media/video/tuner-simple.c
@@ -17,7 +17,7 @@ static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");
-#define PREFIX "tuner-simple "
+#define PREFIX "tuner-simple"
static int offset = 0;
module_param(offset, int, 0664);
@@ -355,10 +355,14 @@ static int simple_set_tv_freq(struct dvb_frontend *fe,
}
priv->last_div = div;
if (t_params->has_tda9887) {
+ struct v4l2_priv_tun_config tda9887_cfg;
int config = 0;
int is_secam_l = (params->std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) &&
!(params->std & ~(V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC));
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &config;
+
if (params->std == V4L2_STD_SECAM_LC) {
if (t_params->port1_active ^ t_params->port1_invert_for_secam_lc)
config |= TDA9887_PORT1_ACTIVE;
@@ -391,7 +395,8 @@ static int simple_set_tv_freq(struct dvb_frontend *fe,
}
if (t_params->default_pll_gating_18)
config |= TDA9887_GATING_18;
- i2c_clients_command(priv->i2c_props.adap, TDA9887_SET_CONFIG, &config);
+ i2c_clients_command(priv->i2c_props.adap, TUNER_SET_CONFIG,
+ &tda9887_cfg);
}
tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n",
buffer[0],buffer[1],buffer[2],buffer[3]);
@@ -534,6 +539,11 @@ static int simple_set_radio_freq(struct dvb_frontend *fe,
if (t_params->has_tda9887) {
int config = 0;
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &config;
+
if (t_params->port1_active && !t_params->port1_fm_high_sensitivity)
config |= TDA9887_PORT1_ACTIVE;
if (t_params->port2_active && !t_params->port2_fm_high_sensitivity)
@@ -546,7 +556,8 @@ static int simple_set_radio_freq(struct dvb_frontend *fe,
config |= TDA9887_GAIN_NORMAL;
if (t_params->radio_if == 2)
config |= TDA9887_RIF_41_3;
- i2c_clients_command(priv->i2c_props.adap, TDA9887_SET_CONFIG, &config);
+ i2c_clients_command(priv->i2c_props.adap, TUNER_SET_CONFIG,
+ &tda9887_cfg);
}
if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,buffer,4)))
tuner_warn("i2c i/o error: rc == %d (should be 4)\n",rc);
diff --git a/drivers/media/video/tuner-types.c b/drivers/media/video/tuner-types.c
index c6a7934bd5a6..883047f9c28c 100644
--- a/drivers/media/video/tuner-types.c
+++ b/drivers/media/video/tuner-types.c
@@ -1366,7 +1366,7 @@ struct tunertype tuners[] = {
.count = ARRAY_SIZE(tuner_philips_fq1286_params),
},
[TUNER_PHILIPS_TDA8290] = { /* Philips PAL|NTSC */
- .name = "tda8290+75",
+ .name = "Philips/NXP TDA 8290/8295 + 8275/8275A/18271",
/* see tda8290.c for details */ },
[TUNER_TCL_2002MB] = { /* TCL PAL */
.name = "TCL 2002MB",
@@ -1452,9 +1452,9 @@ struct tunertype tuners[] = {
.params = tuner_samsung_tcpn_2121p30a_params,
.count = ARRAY_SIZE(tuner_samsung_tcpn_2121p30a_params),
},
- [TUNER_XCEIVE_XC3028] = { /* Xceive 3028 */
- .name = "Xceive xc3028",
- /* see xc3028.c for details */
+ [TUNER_XC2028] = { /* Xceive 2028 */
+ .name = "Xceive xc2028/xc3028 tuner",
+ /* see tuner-xc2028.c for details */
},
[TUNER_THOMSON_FE6600] = { /* Thomson PAL / DVB-T */
.name = "Thomson FE6600",
@@ -1475,6 +1475,10 @@ struct tunertype tuners[] = {
.name = "Philips TEA5761 FM Radio",
/* see tea5767.c for details */
},
+ [TUNER_XC5000] = { /* Xceive 5000 */
+ .name = "Xceive 5000 tuner",
+ /* see xc5000.c for details */
+ },
};
unsigned const int tuner_count = ARRAY_SIZE(tuners);
diff --git a/drivers/media/video/tuner-xc2028-types.h b/drivers/media/video/tuner-xc2028-types.h
new file mode 100644
index 000000000000..d0057fbf0ec7
--- /dev/null
+++ b/drivers/media/video/tuner-xc2028-types.h
@@ -0,0 +1,128 @@
+/* tuner-xc2028_types
+ *
+ * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+/* xc3028 firmware types */
+
+/* BASE firmware should be loaded before any other firmware */
+#define BASE (1<<0)
+#define BASE_TYPES (BASE|F8MHZ|MTS|FM|INPUT1|INPUT2|INIT1)
+
+/* F8MHZ marks BASE firmwares for 8 MHz Bandwidth */
+#define F8MHZ (1<<1)
+
+/* Multichannel Television Sound (MTS)
+ Those firmwares are capable of using xc2038 DSP to decode audio and
+ produce a baseband audio output on some pins of the chip.
+ There are MTS firmwares for the most used video standards. It should be
+ required to use MTS firmwares, depending on the way audio is routed into
+ the bridge chip
+ */
+#define MTS (1<<2)
+
+/* FIXME: I have no idea what's the difference between
+ D2620 and D2633 firmwares
+ */
+#define D2620 (1<<3)
+#define D2633 (1<<4)
+
+/* DTV firmwares for 6, 7 and 8 MHz
+ DTV6 - 6MHz - ATSC/DVB-C/DVB-T/ISDB-T/DOCSIS
+ DTV8 - 8MHz - DVB-C/DVB-T
+ */
+#define DTV6 (1 << 5)
+#define QAM (1 << 6)
+#define DTV7 (1<<7)
+#define DTV78 (1<<8)
+#define DTV8 (1<<9)
+
+#define DTV_TYPES (D2620|D2633|DTV6|QAM|DTV7|DTV78|DTV8|ATSC)
+
+/* There's a FM | BASE firmware + FM specific firmware (std=0) */
+#define FM (1<<10)
+
+#define STD_SPECIFIC_TYPES (MTS|FM|LCD|NOGD)
+
+/* Applies only for FM firmware
+ Makes it use RF input 1 (pin #2) instead of input 2 (pin #4)
+ */
+#define INPUT1 (1<<11)
+
+
+/* LCD firmwares exist only for MTS STD/MN (PAL or NTSC/M)
+ and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr)
+ There are variants both with and without NOGD
+ */
+#define LCD (1<<12)
+
+/* NOGD firmwares exist only for MTS STD/MN (PAL or NTSC/M)
+ and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr)
+ */
+#define NOGD (1<<13)
+
+/* Old firmwares were broken into init0 and init1 */
+#define INIT1 (1<<14)
+
+/* SCODE firmware selects particular behaviours */
+#define MONO (1 << 15)
+#define ATSC (1 << 16)
+#define IF (1 << 17)
+#define LG60 (1 << 18)
+#define ATI638 (1 << 19)
+#define OREN538 (1 << 20)
+#define OREN36 (1 << 21)
+#define TOYOTA388 (1 << 22)
+#define TOYOTA794 (1 << 23)
+#define DIBCOM52 (1 << 24)
+#define ZARLINK456 (1 << 25)
+#define CHINA (1 << 26)
+#define F6MHZ (1 << 27)
+#define INPUT2 (1 << 28)
+#define SCODE (1 << 29)
+
+/* This flag identifies that the scode table has a new format */
+#define HAS_IF (1 << 30)
+
+#define SCODE_TYPES (MTS|DTV6|QAM|DTV7|DTV78|DTV8|LCD|NOGD|MONO|ATSC|IF| \
+ LG60|ATI638|OREN538|OREN36|TOYOTA388|TOYOTA794| \
+ DIBCOM52|ZARLINK456|CHINA|F6MHZ|SCODE)
+
+/* Newer types to be moved to videodev2.h */
+
+#define V4L2_STD_SECAM_K3 (0x04000000)
+
+/* Audio types */
+
+#define V4L2_STD_A2_A (1LL<<32)
+#define V4L2_STD_A2_B (1LL<<33)
+#define V4L2_STD_NICAM_A (1LL<<34)
+#define V4L2_STD_NICAM_B (1LL<<35)
+#define V4L2_STD_AM (1LL<<36)
+#define V4L2_STD_BTSC (1LL<<37)
+#define V4L2_STD_EIAJ (1LL<<38)
+
+#define V4L2_STD_A2 (V4L2_STD_A2_A | V4L2_STD_A2_B)
+#define V4L2_STD_NICAM (V4L2_STD_NICAM_A | V4L2_STD_NICAM_B)
+
+/* To preserve backward compatibilty,
+ (std & V4L2_STD_AUDIO) = 0 means that ALL audio stds are supported
+ */
+
+#define V4L2_STD_AUDIO (V4L2_STD_A2 | \
+ V4L2_STD_NICAM | \
+ V4L2_STD_AM | \
+ V4L2_STD_BTSC | \
+ V4L2_STD_EIAJ)
+
+/* Used standards with audio restrictions */
+
+#define V4L2_STD_PAL_BG_A2_A (V4L2_STD_PAL_BG | V4L2_STD_A2_A)
+#define V4L2_STD_PAL_BG_A2_B (V4L2_STD_PAL_BG | V4L2_STD_A2_B)
+#define V4L2_STD_PAL_BG_NICAM_A (V4L2_STD_PAL_BG | V4L2_STD_NICAM_A)
+#define V4L2_STD_PAL_BG_NICAM_B (V4L2_STD_PAL_BG | V4L2_STD_NICAM_B)
+#define V4L2_STD_PAL_DK_A2 (V4L2_STD_PAL_DK | V4L2_STD_A2)
+#define V4L2_STD_PAL_DK_NICAM (V4L2_STD_PAL_DK | V4L2_STD_NICAM)
+#define V4L2_STD_SECAM_L_NICAM (V4L2_STD_SECAM_L | V4L2_STD_NICAM)
+#define V4L2_STD_SECAM_L_AM (V4L2_STD_SECAM_L | V4L2_STD_AM)
diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/video/tuner-xc2028.c
new file mode 100644
index 000000000000..f191f6a48070
--- /dev/null
+++ b/drivers/media/video/tuner-xc2028.c
@@ -0,0 +1,1213 @@
+/* tuner-xc2028
+ *
+ * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org)
+ *
+ * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com)
+ * - frontend interface
+ *
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+#include <linux/i2c.h>
+#include <asm/div64.h>
+#include <linux/firmware.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <media/tuner.h>
+#include <linux/mutex.h>
+#include "tuner-i2c.h"
+#include "tuner-xc2028.h"
+#include "tuner-xc2028-types.h"
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+
+#define PREFIX "xc2028"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable verbose debug messages");
+
+static char audio_std[8];
+module_param_string(audio_std, audio_std, sizeof(audio_std), 0);
+MODULE_PARM_DESC(audio_std,
+ "Audio standard. XC3028 audio decoder explicitly "
+ "needs to know what audio\n"
+ "standard is needed for some video standards with audio A2 or NICAM.\n"
+ "The valid values are:\n"
+ "A2\n"
+ "A2/A\n"
+ "A2/B\n"
+ "NICAM\n"
+ "NICAM/A\n"
+ "NICAM/B\n");
+
+static LIST_HEAD(xc2028_list);
+static DEFINE_MUTEX(xc2028_list_mutex);
+
+/* struct for storing firmware table */
+struct firmware_description {
+ unsigned int type;
+ v4l2_std_id id;
+ __u16 int_freq;
+ unsigned char *ptr;
+ unsigned int size;
+};
+
+struct firmware_properties {
+ unsigned int type;
+ v4l2_std_id id;
+ v4l2_std_id std_req;
+ __u16 int_freq;
+ unsigned int scode_table;
+ int scode_nr;
+};
+
+struct xc2028_data {
+ struct list_head xc2028_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;
+ int firm_size;
+ __u16 firm_version;
+
+ __u16 hwmodel;
+ __u16 hwvers;
+
+ struct xc2028_ctrl ctrl;
+
+ struct firmware_properties cur_fw;
+
+ struct mutex lock;
+};
+
+#define i2c_send(priv, buf, size) ({ \
+ int _rc; \
+ _rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size); \
+ if (size != _rc) \
+ tuner_info("i2c output error: rc = %d (should be %d)\n",\
+ _rc, (int)size); \
+ _rc; \
+})
+
+#define i2c_rcv(priv, buf, size) ({ \
+ int _rc; \
+ _rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size); \
+ if (size != _rc) \
+ tuner_err("i2c input error: rc = %d (should be %d)\n", \
+ _rc, (int)size); \
+ _rc; \
+})
+
+#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \
+ int _rc; \
+ _rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize, \
+ ibuf, isize); \
+ if (isize != _rc) \
+ tuner_err("i2c input error: rc = %d (should be %d)\n", \
+ _rc, (int)isize); \
+ _rc; \
+})
+
+#define send_seq(priv, data...) ({ \
+ static u8 _val[] = data; \
+ int _rc; \
+ if (sizeof(_val) != \
+ (_rc = tuner_i2c_xfer_send(&priv->i2c_props, \
+ _val, sizeof(_val)))) { \
+ tuner_err("Error on line %d: %d\n", __LINE__, _rc); \
+ } else \
+ msleep(10); \
+ _rc; \
+})
+
+static unsigned int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val)
+{
+ unsigned char buf[2];
+ unsigned char ibuf[2];
+
+ tuner_dbg("%s %04x called\n", __FUNCTION__, reg);
+
+ buf[0] = reg >> 8;
+ buf[1] = (unsigned char) reg;
+
+ if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2)
+ return -EIO;
+
+ *val = (ibuf[1]) | (ibuf[0] << 8);
+ return 0;
+}
+
+#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0)
+void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
+{
+ if (type & BASE)
+ printk("BASE ");
+ if (type & INIT1)
+ printk("INIT1 ");
+ if (type & F8MHZ)
+ printk("F8MHZ ");
+ if (type & MTS)
+ printk("MTS ");
+ if (type & D2620)
+ printk("D2620 ");
+ if (type & D2633)
+ printk("D2633 ");
+ if (type & DTV6)
+ printk("DTV6 ");
+ if (type & QAM)
+ printk("QAM ");
+ if (type & DTV7)
+ printk("DTV7 ");
+ if (type & DTV78)
+ printk("DTV78 ");
+ if (type & DTV8)
+ printk("DTV8 ");
+ if (type & FM)
+ printk("FM ");
+ if (type & INPUT1)
+ printk("INPUT1 ");
+ if (type & LCD)
+ printk("LCD ");
+ if (type & NOGD)
+ printk("NOGD ");
+ if (type & MONO)
+ printk("MONO ");
+ if (type & ATSC)
+ printk("ATSC ");
+ if (type & IF)
+ printk("IF ");
+ if (type & LG60)
+ printk("LG60 ");
+ if (type & ATI638)
+ printk("ATI638 ");
+ if (type & OREN538)
+ printk("OREN538 ");
+ if (type & OREN36)
+ printk("OREN36 ");
+ if (type & TOYOTA388)
+ printk("TOYOTA388 ");
+ if (type & TOYOTA794)
+ printk("TOYOTA794 ");
+ if (type & DIBCOM52)
+ printk("DIBCOM52 ");
+ if (type & ZARLINK456)
+ printk("ZARLINK456 ");
+ if (type & CHINA)
+ printk("CHINA ");
+ if (type & F6MHZ)
+ printk("F6MHZ ");
+ if (type & INPUT2)
+ printk("INPUT2 ");
+ if (type & SCODE)
+ printk("SCODE ");
+ if (type & HAS_IF)
+ printk("HAS_IF_%d ", int_freq);
+}
+
+static v4l2_std_id parse_audio_std_option(void)
+{
+ if (strcasecmp(audio_std, "A2") == 0)
+ return V4L2_STD_A2;
+ if (strcasecmp(audio_std, "A2/A") == 0)
+ return V4L2_STD_A2_A;
+ if (strcasecmp(audio_std, "A2/B") == 0)
+ return V4L2_STD_A2_B;
+ if (strcasecmp(audio_std, "NICAM") == 0)
+ return V4L2_STD_NICAM;
+ if (strcasecmp(audio_std, "NICAM/A") == 0)
+ return V4L2_STD_NICAM_A;
+ if (strcasecmp(audio_std, "NICAM/B") == 0)
+ return V4L2_STD_NICAM_B;
+
+ return 0;
+}
+
+static void free_firmware(struct xc2028_data *priv)
+{
+ int i;
+
+ if (!priv->firm)
+ return;
+
+ for (i = 0; i < priv->firm_size; i++)
+ kfree(priv->firm[i].ptr);
+
+ kfree(priv->firm);
+
+ priv->firm = NULL;
+ priv->firm_size = 0;
+
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+}
+
+static int load_all_firmwares(struct dvb_frontend *fe)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ const struct firmware *fw = NULL;
+ unsigned char *p, *endp;
+ int rc = 0;
+ int n, n_array;
+ char name[33];
+
+ tuner_dbg("%s called\n", __FUNCTION__);
+
+ tuner_dbg("Reading firmware %s\n", priv->ctrl.fname);
+ rc = request_firmware(&fw, priv->ctrl.fname,
+ &priv->i2c_props.adap->dev);
+ if (rc < 0) {
+ if (rc == -ENOENT)
+ tuner_err("Error: firmware %s not found.\n",
+ priv->ctrl.fname);
+ else
+ tuner_err("Error %d while requesting firmware %s \n",
+ rc, priv->ctrl.fname);
+
+ return rc;
+ }
+ p = fw->data;
+ endp = p + fw->size;
+
+ if (fw->size < sizeof(name) - 1 + 2 + 2) {
+ tuner_err("Error: firmware file %s has invalid size!\n",
+ priv->ctrl.fname);
+ goto corrupt;
+ }
+
+ memcpy(name, p, sizeof(name) - 1);
+ name[sizeof(name) - 1] = 0;
+ p += sizeof(name) - 1;
+
+ priv->firm_version = le16_to_cpu(*(__u16 *) p);
+ p += 2;
+
+ n_array = le16_to_cpu(*(__u16 *) p);
+ p += 2;
+
+ tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n",
+ n_array, priv->ctrl.fname, name,
+ priv->firm_version >> 8, priv->firm_version & 0xff);
+
+ priv->firm = kzalloc(sizeof(*priv->firm) * n_array, GFP_KERNEL);
+ if (priv->firm == NULL) {
+ tuner_err("Not enough memory to load firmware file.\n");
+ rc = -ENOMEM;
+ goto err;
+ }
+ priv->firm_size = n_array;
+
+ n = -1;
+ while (p < endp) {
+ __u32 type, size;
+ v4l2_std_id id;
+ __u16 int_freq = 0;
+
+ n++;
+ if (n >= n_array) {
+ tuner_err("More firmware images in file than "
+ "were expected!\n");
+ goto corrupt;
+ }
+
+ /* Checks if there's enough bytes to read */
+ if (p + sizeof(type) + sizeof(id) + sizeof(size) > endp) {
+ tuner_err("Firmware header is incomplete!\n");
+ goto corrupt;
+ }
+
+ type = le32_to_cpu(*(__u32 *) p);
+ p += sizeof(type);
+
+ id = le64_to_cpu(*(v4l2_std_id *) p);
+ p += sizeof(id);
+
+ if (type & HAS_IF) {
+ int_freq = le16_to_cpu(*(__u16 *) p);
+ p += sizeof(int_freq);
+ }
+
+ size = le32_to_cpu(*(__u32 *) p);
+ p += sizeof(size);
+
+ if ((!size) || (size + p > endp)) {
+ tuner_err("Firmware type ");
+ dump_firm_type(type);
+ printk("(%x), id %llx is corrupted "
+ "(size=%d, expected %d)\n",
+ type, (unsigned long long)id,
+ (unsigned)(endp - p), size);
+ goto corrupt;
+ }
+
+ priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
+ if (priv->firm[n].ptr == NULL) {
+ tuner_err("Not enough memory to load firmware file.\n");
+ rc = -ENOMEM;
+ goto err;
+ }
+ tuner_dbg("Reading firmware type ");
+ if (debug) {
+ dump_firm_type_and_int_freq(type, int_freq);
+ printk("(%x), id %llx, size=%d.\n",
+ type, (unsigned long long)id, size);
+ }
+
+ memcpy(priv->firm[n].ptr, p, size);
+ priv->firm[n].type = type;
+ priv->firm[n].id = id;
+ priv->firm[n].size = size;
+ priv->firm[n].int_freq = int_freq;
+
+ p += size;
+ }
+
+ if (n + 1 != priv->firm_size) {
+ tuner_err("Firmware file is incomplete!\n");
+ goto corrupt;
+ }
+
+ goto done;
+
+corrupt:
+ rc = -EINVAL;
+ tuner_err("Error: firmware file is corrupted!\n");
+
+err:
+ tuner_info("Releasing partially loaded firmware file.\n");
+ free_firmware(priv);
+
+done:
+ release_firmware(fw);
+ if (rc == 0)
+ tuner_dbg("Firmware files loaded.\n");
+
+ return rc;
+}
+
+static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int i, best_i = -1, best_nr_matches = 0;
+ unsigned int ign_firm_type_mask = 0;
+
+ tuner_dbg("%s called, want type=", __FUNCTION__);
+ if (debug) {
+ dump_firm_type(type);
+ printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
+ }
+
+ if (!priv->firm) {
+ tuner_err("Error! firmware not loaded\n");
+ return -EINVAL;
+ }
+
+ if (((type & ~SCODE) == 0) && (*id == 0))
+ *id = V4L2_STD_PAL;
+
+ if (type & BASE)
+ type &= BASE_TYPES;
+ else if (type & SCODE) {
+ type &= SCODE_TYPES;
+ ign_firm_type_mask = HAS_IF;
+ } else if (type & DTV_TYPES)
+ type &= DTV_TYPES;
+ else if (type & STD_SPECIFIC_TYPES)
+ type &= STD_SPECIFIC_TYPES;
+
+ /* Seek for exact match */
+ for (i = 0; i < priv->firm_size; i++) {
+ if ((type == (priv->firm[i].type & ~ign_firm_type_mask)) &&
+ (*id == priv->firm[i].id))
+ goto found;
+ }
+
+ /* Seek for generic video standard match */
+ for (i = 0; i < priv->firm_size; i++) {
+ v4l2_std_id match_mask;
+ int nr_matches;
+
+ if (type != (priv->firm[i].type & ~ign_firm_type_mask))
+ continue;
+
+ match_mask = *id & priv->firm[i].id;
+ if (!match_mask)
+ continue;
+
+ if ((*id & match_mask) == *id)
+ goto found; /* Supports all the requested standards */
+
+ nr_matches = hweight64(match_mask);
+ if (nr_matches > best_nr_matches) {
+ best_nr_matches = nr_matches;
+ best_i = i;
+ }
+ }
+
+ if (best_nr_matches > 0) {
+ tuner_dbg("Selecting best matching firmware (%d bits) for "
+ "type=", best_nr_matches);
+ dump_firm_type(type);
+ printk("(%x), id %016llx:\n", type, (unsigned long long)*id);
+ i = best_i;
+ goto found;
+ }
+
+ /*FIXME: Would make sense to seek for type "hint" match ? */
+
+ i = -ENOENT;
+ goto ret;
+
+found:
+ *id = priv->firm[i].id;
+
+ret:
+ tuner_dbg("%s firmware for type=", (i < 0) ? "Can't find" : "Found");
+ if (debug) {
+ dump_firm_type(type);
+ printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
+ }
+ return i;
+}
+
+static int load_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int pos, rc;
+ unsigned char *p, *endp, buf[priv->ctrl.max_len];
+
+ tuner_dbg("%s called\n", __FUNCTION__);
+
+ pos = seek_firmware(fe, type, id);
+ if (pos < 0)
+ return pos;
+
+ tuner_info("Loading firmware for type=");
+ dump_firm_type(priv->firm[pos].type);
+ printk("(%x), id %016llx.\n", priv->firm[pos].type,
+ (unsigned long long)*id);
+
+ p = priv->firm[pos].ptr;
+ endp = p + priv->firm[pos].size;
+
+ while (p < endp) {
+ __u16 size;
+
+ /* Checks if there's enough bytes to read */
+ if (p + sizeof(size) > endp) {
+ tuner_err("Firmware chunk size is wrong\n");
+ return -EINVAL;
+ }
+
+ size = le16_to_cpu(*(__u16 *) p);
+ p += sizeof(size);
+
+ if (size == 0xffff)
+ return 0;
+
+ if (!size) {
+ /* Special callback command received */
+ rc = priv->tuner_callback(priv->video_dev,
+ XC2028_TUNER_RESET, 0);
+ if (rc < 0) {
+ tuner_err("Error at RESET code %d\n",
+ (*p) & 0x7f);
+ return -EINVAL;
+ }
+ continue;
+ }
+ if (size >= 0xff00) {
+ switch (size) {
+ case 0xff00:
+ rc = priv->tuner_callback(priv->video_dev,
+ XC2028_RESET_CLK, 0);
+ if (rc < 0) {
+ tuner_err("Error at RESET code %d\n",
+ (*p) & 0x7f);
+ return -EINVAL;
+ }
+ break;
+ default:
+ tuner_info("Invalid RESET code %d\n",
+ size & 0x7f);
+ return -EINVAL;
+
+ }
+ continue;
+ }
+
+ /* Checks for a sleep command */
+ if (size & 0x8000) {
+ msleep(size & 0x7fff);
+ continue;
+ }
+
+ if ((size + p > endp)) {
+ tuner_err("missing bytes: need %d, have %d\n",
+ size, (int)(endp - p));
+ return -EINVAL;
+ }
+
+ buf[0] = *p;
+ p++;
+ size--;
+
+ /* Sends message chunks */
+ while (size > 0) {
+ int len = (size < priv->ctrl.max_len - 1) ?
+ size : priv->ctrl.max_len - 1;
+
+ memcpy(buf + 1, p, len);
+
+ rc = i2c_send(priv, buf, len + 1);
+ if (rc < 0) {
+ tuner_err("%d returned from send\n", rc);
+ return -EINVAL;
+ }
+
+ p += len;
+ size -= len;
+ }
+ }
+ return 0;
+}
+
+static int load_scode(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id, __u16 int_freq, int scode)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int pos, rc;
+ unsigned char *p;
+
+ tuner_dbg("%s called\n", __FUNCTION__);
+
+ if (!int_freq) {
+ pos = seek_firmware(fe, type, id);
+ if (pos < 0)
+ return pos;
+ } else {
+ for (pos = 0; pos < priv->firm_size; pos++) {
+ if ((priv->firm[pos].int_freq == int_freq) &&
+ (priv->firm[pos].type & HAS_IF))
+ break;
+ }
+ if (pos == priv->firm_size)
+ return -ENOENT;
+ }
+
+ p = priv->firm[pos].ptr;
+
+ if (priv->firm[pos].type & HAS_IF) {
+ if (priv->firm[pos].size != 12 * 16 || scode >= 16)
+ return -EINVAL;
+ p += 12 * scode;
+ } else {
+ /* 16 SCODE entries per file; each SCODE entry is 12 bytes and
+ * has a 2-byte size header in the firmware format. */
+ if (priv->firm[pos].size != 14 * 16 || scode >= 16 ||
+ le16_to_cpu(*(__u16 *)(p + 14 * scode)) != 12)
+ return -EINVAL;
+ p += 14 * scode + 2;
+ }
+
+ tuner_info("Loading SCODE for type=");
+ dump_firm_type_and_int_freq(priv->firm[pos].type,
+ priv->firm[pos].int_freq);
+ printk("(%x), id %016llx.\n", priv->firm[pos].type,
+ (unsigned long long)*id);
+
+ if (priv->firm_version < 0x0202)
+ rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00});
+ else
+ rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00});
+ if (rc < 0)
+ return -EIO;
+
+ rc = i2c_send(priv, p, 12);
+ if (rc < 0)
+ return -EIO;
+
+ rc = send_seq(priv, {0x00, 0x8c});
+ if (rc < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int check_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id std, __u16 int_freq)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ struct firmware_properties new_fw;
+ int rc = 0, is_retry = 0;
+ u16 version, hwmodel;
+ v4l2_std_id std0;
+
+ tuner_dbg("%s called\n", __FUNCTION__);
+
+ if (!priv->firm) {
+ if (!priv->ctrl.fname) {
+ tuner_info("xc2028/3028 firmware name not set!\n");
+ return -EINVAL;
+ }
+
+ rc = load_all_firmwares(fe);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (priv->ctrl.mts && !(type & FM))
+ type |= MTS;
+
+retry:
+ new_fw.type = type;
+ new_fw.id = std;
+ new_fw.std_req = std;
+ new_fw.scode_table = SCODE | priv->ctrl.scode_table;
+ new_fw.scode_nr = 0;
+ new_fw.int_freq = int_freq;
+
+ tuner_dbg("checking firmware, user requested type=");
+ if (debug) {
+ dump_firm_type(new_fw.type);
+ printk("(%x), id %016llx, ", new_fw.type,
+ (unsigned long long)new_fw.std_req);
+ if (!int_freq) {
+ printk("scode_tbl ");
+ dump_firm_type(priv->ctrl.scode_table);
+ printk("(%x), ", priv->ctrl.scode_table);
+ } else
+ printk("int_freq %d, ", new_fw.int_freq);
+ printk("scode_nr %d\n", new_fw.scode_nr);
+ }
+
+ /* No need to reload base firmware if it matches */
+ if (((BASE | new_fw.type) & BASE_TYPES) ==
+ (priv->cur_fw.type & BASE_TYPES)) {
+ tuner_dbg("BASE firmware not changed.\n");
+ goto skip_base;
+ }
+
+ /* Updating BASE - forget about all currently loaded firmware */
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+
+ /* Reset is needed before loading firmware */
+ rc = priv->tuner_callback(priv->video_dev,
+ XC2028_TUNER_RESET, 0);
+ if (rc < 0)
+ goto fail;
+
+ /* BASE firmwares are all std0 */
+ std0 = 0;
+ rc = load_firmware(fe, BASE | new_fw.type, &std0);
+ if (rc < 0) {
+ tuner_err("Error %d while loading base firmware\n",
+ rc);
+ goto fail;
+ }
+
+ /* Load INIT1, if needed */
+ tuner_dbg("Load init1 firmware, if exists\n");
+
+ rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0);
+ if (rc == -ENOENT)
+ rc = load_firmware(fe, (BASE | INIT1 | new_fw.type) & ~F8MHZ,
+ &std0);
+ if (rc < 0 && rc != -ENOENT) {
+ tuner_err("Error %d while loading init1 firmware\n",
+ rc);
+ goto fail;
+ }
+
+skip_base:
+ /*
+ * No need to reload standard specific firmware if base firmware
+ * was not reloaded and requested video standards have not changed.
+ */
+ if (priv->cur_fw.type == (BASE | new_fw.type) &&
+ priv->cur_fw.std_req == std) {
+ tuner_dbg("Std-specific firmware already loaded.\n");
+ goto skip_std_specific;
+ }
+
+ /* Reloading std-specific firmware forces a SCODE update */
+ priv->cur_fw.scode_table = 0;
+
+ rc = load_firmware(fe, new_fw.type, &new_fw.id);
+ if (rc == -ENOENT)
+ rc = load_firmware(fe, new_fw.type & ~F8MHZ, &new_fw.id);
+
+ if (rc < 0)
+ goto fail;
+
+skip_std_specific:
+ if (priv->cur_fw.scode_table == new_fw.scode_table &&
+ priv->cur_fw.scode_nr == new_fw.scode_nr) {
+ tuner_dbg("SCODE firmware already loaded.\n");
+ goto check_device;
+ }
+
+ /* Load SCODE firmware, if exists */
+ tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr);
+
+ rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,
+ new_fw.int_freq, new_fw.scode_nr);
+
+check_device:
+ if (xc2028_get_reg(priv, 0x0004, &version) < 0 ||
+ xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) {
+ tuner_err("Unable to read tuner registers.\n");
+ goto fail;
+ }
+
+ tuner_info("Device is Xceive %d version %d.%d, "
+ "firmware version %d.%d\n",
+ hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
+ (version & 0xf0) >> 4, version & 0xf);
+
+ /* Check firmware version against what we downloaded. */
+ if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) {
+ tuner_err("Incorrect readback of firmware version.\n");
+ goto fail;
+ }
+
+ /* Check that the tuner hardware model remains consistent over time. */
+ if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) {
+ priv->hwmodel = hwmodel;
+ priv->hwvers = version & 0xff00;
+ } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
+ priv->hwvers != (version & 0xff00)) {
+ tuner_err("Read invalid device hardware information - tuner "
+ "hung?\n");
+ goto fail;
+ }
+
+ memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
+
+ /*
+ * By setting BASE in cur_fw.type only after successfully loading all
+ * firmwares, we can:
+ * 1. Identify that BASE firmware with type=0 has been loaded;
+ * 2. Tell whether BASE firmware was just changed the next time through.
+ */
+ priv->cur_fw.type |= BASE;
+
+ return 0;
+
+fail:
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+ if (!is_retry) {
+ msleep(50);
+ is_retry = 1;
+ tuner_dbg("Retrying firmware load\n");
+ goto retry;
+ }
+
+ if (rc == -ENOENT)
+ rc = -EINVAL;
+ return rc;
+}
+
+static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ u16 frq_lock, signal = 0;
+ int rc;
+
+ tuner_dbg("%s called\n", __FUNCTION__);
+
+ mutex_lock(&priv->lock);
+
+ /* Sync Lock Indicator */
+ rc = xc2028_get_reg(priv, 0x0002, &frq_lock);
+ if (rc < 0 || frq_lock == 0)
+ goto ret;
+
+ /* Frequency is locked. Return signal quality */
+
+ /* Get SNR of the video signal */
+ rc = xc2028_get_reg(priv, 0x0040, &signal);
+ if (rc < 0)
+ signal = -frq_lock;
+
+ret:
+ mutex_unlock(&priv->lock);
+
+ *strength = signal;
+
+ return rc;
+}
+
+#define DIV 15625
+
+static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
+ enum tuner_mode new_mode,
+ unsigned int type,
+ v4l2_std_id std,
+ u16 int_freq)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int rc = -EINVAL;
+ unsigned char buf[4];
+ u32 div, offset = 0;
+
+ tuner_dbg("%s called\n", __FUNCTION__);
+
+ mutex_lock(&priv->lock);
+
+ tuner_dbg("should set frequency %d kHz\n", freq / 1000);
+
+ if (check_firmware(fe, type, std, int_freq) < 0)
+ goto ret;
+
+ /* On some cases xc2028 can disable video output, if
+ * very weak signals are received. By sending a soft
+ * reset, this is re-enabled. So, it is better to always
+ * send a soft reset before changing channels, to be sure
+ * that xc2028 will be in a safe state.
+ * Maybe this might also be needed for DTV.
+ */
+ if (new_mode == T_ANALOG_TV) {
+ rc = send_seq(priv, {0x00, 0x00});
+ } else if (priv->cur_fw.type & ATSC) {
+ offset = 1750000;
+ } else {
+ offset = 2750000;
+ /*
+ * We must adjust the offset by 500kHz in two cases in order
+ * to correctly center the IF output:
+ * 1) When the ZARLINK456 or DIBCOM52 tables were explicitly
+ * selected and a 7MHz channel is tuned;
+ * 2) When tuning a VHF channel with DTV78 firmware.
+ */
+ if (((priv->cur_fw.type & DTV7) &&
+ (priv->cur_fw.scode_table & (ZARLINK456 | DIBCOM52))) ||
+ ((priv->cur_fw.type & DTV78) && freq < 470000000))
+ offset -= 500000;
+ }
+
+ div = (freq - offset + DIV / 2) / DIV;
+
+ /* CMD= Set frequency */
+ if (priv->firm_version < 0x0202)
+ rc = send_seq(priv, {0x00, 0x02, 0x00, 0x00});
+ else
+ rc = send_seq(priv, {0x80, 0x02, 0x00, 0x00});
+ if (rc < 0)
+ goto ret;
+
+ rc = priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
+ if (rc < 0)
+ goto ret;
+
+ msleep(10);
+
+ buf[0] = 0xff & (div >> 24);
+ buf[1] = 0xff & (div >> 16);
+ buf[2] = 0xff & (div >> 8);
+ buf[3] = 0xff & (div);
+
+ rc = i2c_send(priv, buf, sizeof(buf));
+ if (rc < 0)
+ goto ret;
+ msleep(100);
+
+ priv->frequency = freq;
+
+ tuner_dbg("divisor= %02x %02x %02x %02x (freq=%d.%03d)\n",
+ buf[0], buf[1], buf[2], buf[3],
+ freq / 1000000, (freq % 1000000) / 1000);
+
+ rc = 0;
+
+ret:
+ mutex_unlock(&priv->lock);
+
+ return rc;
+}
+
+static int xc2028_set_analog_freq(struct dvb_frontend *fe,
+ struct analog_parameters *p)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ unsigned int type=0;
+
+ tuner_dbg("%s called\n", __FUNCTION__);
+
+ if (p->mode == V4L2_TUNER_RADIO) {
+ type |= FM;
+ if (priv->ctrl.input1)
+ type |= INPUT1;
+ return generic_set_freq(fe, (625l * p->frequency) / 10,
+ T_ANALOG_TV, type, 0, 0);
+ }
+
+ /* if std is not defined, choose one */
+ if (!p->std)
+ p->std = V4L2_STD_MN;
+
+ /* PAL/M, PAL/N, PAL/Nc and NTSC variants should use 6MHz firmware */
+ if (!(p->std & V4L2_STD_MN))
+ type |= F8MHZ;
+
+ /* Add audio hack to std mask */
+ p->std |= parse_audio_std_option();
+
+ return generic_set_freq(fe, 62500l * p->frequency,
+ T_ANALOG_TV, type, p->std, 0);
+}
+
+static int xc2028_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ unsigned int type=0;
+ fe_bandwidth_t bw = BANDWIDTH_8_MHZ;
+ u16 demod = 0;
+
+ tuner_dbg("%s called\n", __FUNCTION__);
+
+ if (priv->ctrl.d2633)
+ type |= D2633;
+ else
+ type |= D2620;
+
+ switch(fe->ops.info.type) {
+ case FE_OFDM:
+ bw = p->u.ofdm.bandwidth;
+ break;
+ case FE_QAM:
+ tuner_info("WARN: There are some reports that "
+ "QAM 6 MHz doesn't work.\n"
+ "If this works for you, please report by "
+ "e-mail to: v4l-dvb-maintainer@linuxtv.org\n");
+ bw = BANDWIDTH_6_MHZ;
+ type |= QAM;
+ break;
+ case FE_ATSC:
+ bw = BANDWIDTH_6_MHZ;
+ /* The only ATSC firmware (at least on v2.7) is D2633,
+ so overrides ctrl->d2633 */
+ type |= ATSC| D2633;
+ type &= ~D2620;
+ break;
+ /* DVB-S is not supported */
+ default:
+ return -EINVAL;
+ }
+
+ switch (bw) {
+ case BANDWIDTH_8_MHZ:
+ if (p->frequency < 470000000)
+ priv->ctrl.vhfbw7 = 0;
+ else
+ priv->ctrl.uhfbw8 = 1;
+ type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV8;
+ type |= F8MHZ;
+ break;
+ case BANDWIDTH_7_MHZ:
+ if (p->frequency < 470000000)
+ priv->ctrl.vhfbw7 = 1;
+ else
+ priv->ctrl.uhfbw8 = 0;
+ type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV7;
+ type |= F8MHZ;
+ break;
+ case BANDWIDTH_6_MHZ:
+ type |= DTV6;
+ priv->ctrl.vhfbw7 = 0;
+ priv->ctrl.uhfbw8 = 0;
+ break;
+ default:
+ tuner_err("error: bandwidth not supported.\n");
+ };
+
+ /* All S-code tables need a 200kHz shift */
+ if (priv->ctrl.demod)
+ demod = priv->ctrl.demod + 200;
+
+ return generic_set_freq(fe, p->frequency,
+ T_DIGITAL_TV, type, 0, demod);
+}
+
+static int xc2028_sleep(struct dvb_frontend *fe)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int rc = 0;
+
+ tuner_dbg("%s called\n", __FUNCTION__);
+
+ mutex_lock(&priv->lock);
+
+ if (priv->firm_version < 0x0202)
+ rc = send_seq(priv, {0x00, 0x08, 0x00, 0x00});
+ else
+ rc = send_seq(priv, {0x80, 0x08, 0x00, 0x00});
+
+ priv->cur_fw.type = 0; /* need firmware reload */
+
+ mutex_unlock(&priv->lock);
+
+ return rc;
+}
+
+
+static int xc2028_dvb_release(struct dvb_frontend *fe)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+
+ tuner_dbg("%s called\n", __FUNCTION__);
+
+ mutex_lock(&xc2028_list_mutex);
+
+ priv->count--;
+
+ if (!priv->count) {
+ list_del(&priv->xc2028_list);
+
+ kfree(priv->ctrl.fname);
+
+ free_firmware(priv);
+ kfree(priv);
+ fe->tuner_priv = NULL;
+ }
+
+ mutex_unlock(&xc2028_list_mutex);
+
+ return 0;
+}
+
+static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+
+ tuner_dbg("%s called\n", __FUNCTION__);
+
+ *frequency = priv->frequency;
+
+ return 0;
+}
+
+static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ struct xc2028_ctrl *p = priv_cfg;
+ int rc = 0;
+
+ tuner_dbg("%s called\n", __FUNCTION__);
+
+ mutex_lock(&priv->lock);
+
+ kfree(priv->ctrl.fname);
+ free_firmware(priv);
+
+ memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
+ priv->ctrl.fname = NULL;
+
+ if (p->fname) {
+ priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL);
+ if (priv->ctrl.fname == NULL)
+ rc = -ENOMEM;
+ }
+
+ if (priv->ctrl.max_len < 9)
+ priv->ctrl.max_len = 13;
+
+ mutex_unlock(&priv->lock);
+
+ return rc;
+}
+
+static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
+ .info = {
+ .name = "Xceive XC3028",
+ .frequency_min = 42000000,
+ .frequency_max = 864000000,
+ .frequency_step = 50000,
+ },
+
+ .set_config = xc2028_set_config,
+ .set_analog_params = xc2028_set_analog_freq,
+ .release = xc2028_dvb_release,
+ .get_frequency = xc2028_get_frequency,
+ .get_rf_strength = xc2028_signal,
+ .set_params = xc2028_set_params,
+ .sleep = xc2028_sleep,
+
+};
+
+struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
+ struct xc2028_config *cfg)
+{
+ struct xc2028_data *priv;
+ void *video_dev;
+
+ if (debug)
+ printk(KERN_DEBUG PREFIX ": Xcv2028/3028 init called!\n");
+
+ if (NULL == cfg || NULL == cfg->video_dev)
+ return NULL;
+
+ if (!fe) {
+ printk(KERN_ERR PREFIX ": No frontend!\n");
+ return NULL;
+ }
+
+ video_dev = cfg->video_dev;
+
+ mutex_lock(&xc2028_list_mutex);
+
+ list_for_each_entry(priv, &xc2028_list, xc2028_list) {
+ if (priv->video_dev == cfg->video_dev) {
+ video_dev = NULL;
+ 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->video_dev = video_dev;
+ 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++;
+
+ memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
+ sizeof(xc2028_dvb_tuner_ops));
+
+ tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");
+
+ if (cfg->ctrl)
+ xc2028_set_config(fe, cfg->ctrl);
+
+ mutex_unlock(&xc2028_list_mutex);
+
+ return fe;
+}
+
+EXPORT_SYMBOL(xc2028_attach);
+
+MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
+MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tuner-xc2028.h b/drivers/media/video/tuner-xc2028.h
new file mode 100644
index 000000000000..3eb8420379a4
--- /dev/null
+++ b/drivers/media/video/tuner-xc2028.h
@@ -0,0 +1,63 @@
+/* tuner-xc2028
+ *
+ * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+#ifndef __TUNER_XC2028_H__
+#define __TUNER_XC2028_H__
+
+#include "dvb_frontend.h"
+
+#define XC2028_DEFAULT_FIRMWARE "xc3028-v27.fw"
+
+/* Dmoduler IF (kHz) */
+#define XC3028_FE_DEFAULT 0
+#define XC3028_FE_LG60 6000
+#define XC3028_FE_ATI638 6380
+#define XC3028_FE_OREN538 5380
+#define XC3028_FE_OREN36 3600
+#define XC3028_FE_TOYOTA388 3880
+#define XC3028_FE_TOYOTA794 7940
+#define XC3028_FE_DIBCOM52 5200
+#define XC3028_FE_ZARLINK456 4560
+#define XC3028_FE_CHINA 5200
+
+struct xc2028_ctrl {
+ char *fname;
+ int max_len;
+ unsigned int scode_table;
+ unsigned int mts :1;
+ unsigned int d2633 :1;
+ unsigned int input1:1;
+ unsigned int vhfbw7:1;
+ unsigned int uhfbw8:1;
+ unsigned int demod;
+};
+
+struct xc2028_config {
+ struct i2c_adapter *i2c_adap;
+ u8 i2c_addr;
+ void *video_dev;
+ struct xc2028_ctrl *ctrl;
+ int (*callback) (void *dev, int command, int arg);
+};
+
+/* xc2028 commands for callback */
+#define XC2028_TUNER_RESET 0
+#define XC2028_RESET_CLK 1
+
+#if defined(CONFIG_TUNER_XC2028) || (defined(CONFIG_TUNER_XC2028_MODULE) && defined(MODULE))
+extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
+ struct xc2028_config *cfg);
+#else
+static inline struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
+ struct xc2028_config *cfg)
+{
+ printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
+ __FUNCTION__);
+ return NULL;
+}
+#endif
+
+#endif /* __TUNER_XC2028_H__ */
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index a19cdcc17ef7..a75560540e79 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -31,6 +31,7 @@
#include <media/tvaudio.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include <media/i2c-addr.h>
@@ -109,7 +110,7 @@ static struct CHIPDESC chiplist[];
/* current state of the chip */
struct CHIPSTATE {
- struct i2c_client c;
+ struct i2c_client *c;
/* index into CHIPDESC array */
int type;
@@ -145,10 +146,6 @@ static unsigned short normal_i2c[] = {
I2C_CLIENT_END };
I2C_CLIENT_INSMOD;
-static struct i2c_driver driver;
-static struct i2c_client client_template;
-
-
/* ---------------------------------------------------------------------- */
/* i2c I/O functions */
@@ -157,24 +154,24 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
unsigned char buffer[2];
if (-1 == subaddr) {
- v4l_dbg(1, debug, &chip->c, "%s: chip_write: 0x%x\n",
- chip->c.name, val);
+ v4l_dbg(1, debug, chip->c, "%s: chip_write: 0x%x\n",
+ chip->c->name, val);
chip->shadow.bytes[1] = val;
buffer[0] = val;
- if (1 != i2c_master_send(&chip->c,buffer,1)) {
- v4l_warn(&chip->c, "%s: I/O error (write 0x%x)\n",
- chip->c.name, val);
+ if (1 != i2c_master_send(chip->c,buffer,1)) {
+ v4l_warn(chip->c, "%s: I/O error (write 0x%x)\n",
+ chip->c->name, val);
return -1;
}
} else {
- v4l_dbg(1, debug, &chip->c, "%s: chip_write: reg%d=0x%x\n",
- chip->c.name, subaddr, val);
+ v4l_dbg(1, debug, chip->c, "%s: chip_write: reg%d=0x%x\n",
+ chip->c->name, subaddr, val);
chip->shadow.bytes[subaddr+1] = val;
buffer[0] = subaddr;
buffer[1] = val;
- if (2 != i2c_master_send(&chip->c,buffer,2)) {
- v4l_warn(&chip->c, "%s: I/O error (write reg%d=0x%x)\n",
- chip->c.name, subaddr, val);
+ if (2 != i2c_master_send(chip->c,buffer,2)) {
+ v4l_warn(chip->c, "%s: I/O error (write reg%d=0x%x)\n",
+ chip->c->name, subaddr, val);
return -1;
}
}
@@ -197,12 +194,12 @@ static int chip_read(struct CHIPSTATE *chip)
{
unsigned char buffer;
- if (1 != i2c_master_recv(&chip->c,&buffer,1)) {
- v4l_warn(&chip->c, "%s: I/O error (read)\n",
- chip->c.name);
+ if (1 != i2c_master_recv(chip->c,&buffer,1)) {
+ v4l_warn(chip->c, "%s: I/O error (read)\n",
+ chip->c->name);
return -1;
}
- v4l_dbg(1, debug, &chip->c, "%s: chip_read: 0x%x\n",chip->c.name, buffer);
+ v4l_dbg(1, debug, chip->c, "%s: chip_read: 0x%x\n",chip->c->name, buffer);
return buffer;
}
@@ -211,17 +208,17 @@ static int chip_read2(struct CHIPSTATE *chip, int subaddr)
unsigned char write[1];
unsigned char read[1];
struct i2c_msg msgs[2] = {
- { chip->c.addr, 0, 1, write },
- { chip->c.addr, I2C_M_RD, 1, read }
+ { chip->c->addr, 0, 1, write },
+ { chip->c->addr, I2C_M_RD, 1, read }
};
write[0] = subaddr;
- if (2 != i2c_transfer(chip->c.adapter,msgs,2)) {
- v4l_warn(&chip->c, "%s: I/O error (read2)\n", chip->c.name);
+ if (2 != i2c_transfer(chip->c->adapter,msgs,2)) {
+ v4l_warn(chip->c, "%s: I/O error (read2)\n", chip->c->name);
return -1;
}
- v4l_dbg(1, debug, &chip->c, "%s: chip_read2: reg%d=0x%x\n",
- chip->c.name, subaddr,read[0]);
+ v4l_dbg(1, debug, chip->c, "%s: chip_read2: reg%d=0x%x\n",
+ chip->c->name, subaddr,read[0]);
return read[0];
}
@@ -233,8 +230,8 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
return 0;
/* update our shadow register set; print bytes if (debug > 0) */
- v4l_dbg(1, debug, &chip->c, "%s: chip_cmd(%s): reg=%d, data:",
- chip->c.name, name,cmd->bytes[0]);
+ v4l_dbg(1, debug, chip->c, "%s: chip_cmd(%s): reg=%d, data:",
+ chip->c->name, name,cmd->bytes[0]);
for (i = 1; i < cmd->count; i++) {
if (debug)
printk(" 0x%x",cmd->bytes[i]);
@@ -244,8 +241,8 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
printk("\n");
/* send data to the chip */
- if (cmd->count != i2c_master_send(&chip->c,cmd->bytes,cmd->count)) {
- v4l_warn(&chip->c, "%s: I/O error (%s)\n", chip->c.name, name);
+ if (cmd->count != i2c_master_send(chip->c,cmd->bytes,cmd->count)) {
+ v4l_warn(chip->c, "%s: I/O error (%s)\n", chip->c->name, name);
return -1;
}
return 0;
@@ -269,7 +266,7 @@ static int chip_thread(void *data)
struct CHIPSTATE *chip = data;
struct CHIPDESC *desc = chiplist + chip->type;
- v4l_dbg(1, debug, &chip->c, "%s: thread started\n", chip->c.name);
+ v4l_dbg(1, debug, chip->c, "%s: thread started\n", chip->c->name);
set_freezable();
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
@@ -279,7 +276,7 @@ static int chip_thread(void *data)
try_to_freeze();
if (kthread_should_stop())
break;
- v4l_dbg(1, debug, &chip->c, "%s: thread wakeup\n", chip->c.name);
+ v4l_dbg(1, debug, chip->c, "%s: thread wakeup\n", chip->c->name);
/* don't do anything for radio or if mode != auto */
if (chip->radio || chip->mode != 0)
@@ -292,7 +289,7 @@ static int chip_thread(void *data)
mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
}
- v4l_dbg(1, debug, &chip->c, "%s: thread exiting\n", chip->c.name);
+ v4l_dbg(1, debug, chip->c, "%s: thread exiting\n", chip->c->name);
return 0;
}
@@ -304,17 +301,19 @@ static void generic_checkmode(struct CHIPSTATE *chip)
if (mode == chip->prevmode)
return;
- v4l_dbg(1, debug, &chip->c, "%s: thread checkmode\n", chip->c.name);
+ v4l_dbg(1, debug, chip->c, "%s: thread checkmode\n", chip->c->name);
chip->prevmode = mode;
- if (mode & VIDEO_SOUND_STEREO)
- desc->setmode(chip,VIDEO_SOUND_STEREO);
- else if (mode & VIDEO_SOUND_LANG1)
- desc->setmode(chip,VIDEO_SOUND_LANG1);
- else if (mode & VIDEO_SOUND_LANG2)
- desc->setmode(chip,VIDEO_SOUND_LANG2);
+ if (mode & V4L2_TUNER_MODE_STEREO)
+ desc->setmode(chip,V4L2_TUNER_MODE_STEREO);
+ if (mode & V4L2_TUNER_MODE_LANG1_LANG2)
+ desc->setmode(chip,V4L2_TUNER_MODE_STEREO);
+ else if (mode & V4L2_TUNER_MODE_LANG1)
+ desc->setmode(chip,V4L2_TUNER_MODE_LANG1);
+ else if (mode & V4L2_TUNER_MODE_LANG2)
+ desc->setmode(chip,V4L2_TUNER_MODE_LANG2);
else
- desc->setmode(chip,VIDEO_SOUND_MONO);
+ desc->setmode(chip,V4L2_TUNER_MODE_MONO);
}
/* ---------------------------------------------------------------------- */
@@ -345,13 +344,13 @@ static int tda9840_getmode(struct CHIPSTATE *chip)
int val, mode;
val = chip_read(chip);
- mode = VIDEO_SOUND_MONO;
+ mode = V4L2_TUNER_MODE_MONO;
if (val & TDA9840_DS_DUAL)
- mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+ mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
if (val & TDA9840_ST_STEREO)
- mode |= VIDEO_SOUND_STEREO;
+ mode |= V4L2_TUNER_MODE_STEREO;
- v4l_dbg(1, debug, &chip->c, "tda9840_getmode(): raw chip read: %d, return: %d\n",
+ v4l_dbg(1, debug, chip->c, "tda9840_getmode(): raw chip read: %d, return: %d\n",
val, mode);
return mode;
}
@@ -362,16 +361,16 @@ static void tda9840_setmode(struct CHIPSTATE *chip, int mode)
int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e;
switch (mode) {
- case VIDEO_SOUND_MONO:
+ case V4L2_TUNER_MODE_MONO:
t |= TDA9840_MONO;
break;
- case VIDEO_SOUND_STEREO:
+ case V4L2_TUNER_MODE_STEREO:
t |= TDA9840_STEREO;
break;
- case VIDEO_SOUND_LANG1:
+ case V4L2_TUNER_MODE_LANG1:
t |= TDA9840_DUALA;
break;
- case VIDEO_SOUND_LANG2:
+ case V4L2_TUNER_MODE_LANG2:
t |= TDA9840_DUALB;
break;
default:
@@ -502,7 +501,7 @@ static int tda985x_getmode(struct CHIPSTATE *chip)
chip_read(chip)) >> 4;
/* Add mono mode regardless of SAP and stereo */
/* Allows forced mono */
- return mode | VIDEO_SOUND_MONO;
+ return mode | V4L2_TUNER_MODE_MONO;
}
static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
@@ -511,13 +510,13 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f;
switch (mode) {
- case VIDEO_SOUND_MONO:
+ case V4L2_TUNER_MODE_MONO:
c6 |= TDA985x_MONO;
break;
- case VIDEO_SOUND_STEREO:
+ case V4L2_TUNER_MODE_STEREO:
c6 |= TDA985x_STEREO;
break;
- case VIDEO_SOUND_LANG1:
+ case V4L2_TUNER_MODE_LANG1:
c6 |= TDA985x_SAP;
break;
default:
@@ -650,12 +649,12 @@ static int tda9873_getmode(struct CHIPSTATE *chip)
int val,mode;
val = chip_read(chip);
- mode = VIDEO_SOUND_MONO;
+ mode = V4L2_TUNER_MODE_MONO;
if (val & TDA9873_STEREO)
- mode |= VIDEO_SOUND_STEREO;
+ mode |= V4L2_TUNER_MODE_STEREO;
if (val & TDA9873_DUAL)
- mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
- v4l_dbg(1, debug, &chip->c, "tda9873_getmode(): raw chip read: %d, return: %d\n",
+ mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ v4l_dbg(1, debug, chip->c, "tda9873_getmode(): raw chip read: %d, return: %d\n",
val, mode);
return mode;
}
@@ -666,24 +665,24 @@ static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
/* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */
if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) {
- v4l_dbg(1, debug, &chip->c, "tda9873_setmode(): external input\n");
+ v4l_dbg(1, debug, chip->c, "tda9873_setmode(): external input\n");
return;
}
- v4l_dbg(1, debug, &chip->c, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
- v4l_dbg(1, debug, &chip->c, "tda9873_setmode(): sw_data = %d\n", sw_data);
+ v4l_dbg(1, debug, chip->c, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
+ v4l_dbg(1, debug, chip->c, "tda9873_setmode(): sw_data = %d\n", sw_data);
switch (mode) {
- case VIDEO_SOUND_MONO:
+ case V4L2_TUNER_MODE_MONO:
sw_data |= TDA9873_TR_MONO;
break;
- case VIDEO_SOUND_STEREO:
+ case V4L2_TUNER_MODE_STEREO:
sw_data |= TDA9873_TR_STEREO;
break;
- case VIDEO_SOUND_LANG1:
+ case V4L2_TUNER_MODE_LANG1:
sw_data |= TDA9873_TR_DUALA;
break;
- case VIDEO_SOUND_LANG2:
+ case V4L2_TUNER_MODE_LANG2:
sw_data |= TDA9873_TR_DUALB;
break;
default:
@@ -692,7 +691,7 @@ static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
}
chip_write(chip, TDA9873_SW, sw_data);
- v4l_dbg(1, debug, &chip->c, "tda9873_setmode(): req. mode %d; chip_write: %d\n",
+ v4l_dbg(1, debug, chip->c, "tda9873_setmode(): req. mode %d; chip_write: %d\n",
mode, sw_data);
}
@@ -831,7 +830,7 @@ static int tda9874a_setup(struct CHIPSTATE *chip)
chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80);
chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */
}
- v4l_dbg(1, debug, &chip->c, "tda9874a_setup(): %s [0x%02X].\n",
+ v4l_dbg(1, debug, chip->c, "tda9874a_setup(): %s [0x%02X].\n",
tda9874a_modelist[tda9874a_STD].name,tda9874a_STD);
return 1;
}
@@ -841,7 +840,7 @@ static int tda9874a_getmode(struct CHIPSTATE *chip)
int dsr,nsr,mode;
int necr; /* just for debugging */
- mode = VIDEO_SOUND_MONO;
+ mode = V4L2_TUNER_MODE_MONO;
if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR)))
return mode;
@@ -860,21 +859,21 @@ static int tda9874a_getmode(struct CHIPSTATE *chip)
* that sound has (temporarily) switched from NICAM to
* mono FM (or AM) on 1st sound carrier due to high NICAM bit
* error count. So in fact there is no stereo in this case :-(
- * But changing the mode to VIDEO_SOUND_MONO would switch
+ * But changing the mode to V4L2_TUNER_MODE_MONO would switch
* external 4052 multiplexer in audio_hook().
*/
if(nsr & 0x02) /* NSR.S/MB=1 */
- mode |= VIDEO_SOUND_STEREO;
+ mode |= V4L2_TUNER_MODE_STEREO;
if(nsr & 0x01) /* NSR.D/SB=1 */
- mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+ mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
} else {
if(dsr & 0x02) /* DSR.IDSTE=1 */
- mode |= VIDEO_SOUND_STEREO;
+ mode |= V4L2_TUNER_MODE_STEREO;
if(dsr & 0x04) /* DSR.IDDUA=1 */
- mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+ mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
}
- v4l_dbg(1, debug, &chip->c, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
+ v4l_dbg(1, debug, chip->c, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
dsr, nsr, necr, mode);
return mode;
}
@@ -902,14 +901,14 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
int mdacosr = (tda9874a_mode) ? 0x82:0x80;
switch(mode) {
- case VIDEO_SOUND_MONO:
- case VIDEO_SOUND_STEREO:
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_STEREO:
break;
- case VIDEO_SOUND_LANG1:
+ case V4L2_TUNER_MODE_LANG1:
aosr = 0x80; /* auto-select, dual A/A */
mdacosr = (tda9874a_mode) ? 0x82:0x80;
break;
- case VIDEO_SOUND_LANG2:
+ case V4L2_TUNER_MODE_LANG2:
aosr = 0xa0; /* auto-select, dual B/B */
mdacosr = (tda9874a_mode) ? 0x83:0x81;
break;
@@ -920,18 +919,18 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
chip_write(chip, TDA9874A_AOSR, aosr);
chip_write(chip, TDA9874A_MDACOSR, mdacosr);
- v4l_dbg(1, debug, &chip->c, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
+ v4l_dbg(1, debug, chip->c, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
mode, aosr, mdacosr);
} else { /* dic == 0x07 */
int fmmr,aosr;
switch(mode) {
- case VIDEO_SOUND_MONO:
+ case V4L2_TUNER_MODE_MONO:
fmmr = 0x00; /* mono */
aosr = 0x10; /* A/A */
break;
- case VIDEO_SOUND_STEREO:
+ case V4L2_TUNER_MODE_STEREO:
if(tda9874a_mode) {
fmmr = 0x00;
aosr = 0x00; /* handled by NICAM auto-mute */
@@ -940,11 +939,11 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
aosr = 0x00;
}
break;
- case VIDEO_SOUND_LANG1:
+ case V4L2_TUNER_MODE_LANG1:
fmmr = 0x02; /* dual */
aosr = 0x10; /* dual A/A */
break;
- case VIDEO_SOUND_LANG2:
+ case V4L2_TUNER_MODE_LANG2:
fmmr = 0x02; /* dual */
aosr = 0x20; /* dual B/B */
break;
@@ -955,7 +954,7 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
chip_write(chip, TDA9874A_FMMR, fmmr);
chip_write(chip, TDA9874A_AOSR, aosr);
- v4l_dbg(1, debug, &chip->c, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
+ v4l_dbg(1, debug, chip->c, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
mode, fmmr, aosr);
}
}
@@ -969,10 +968,10 @@ static int tda9874a_checkit(struct CHIPSTATE *chip)
if(-1 == (sic = chip_read2(chip,TDA9874A_SIC)))
return 0;
- v4l_dbg(1, debug, &chip->c, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic);
+ v4l_dbg(1, debug, chip->c, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic);
if((dic == 0x11)||(dic == 0x07)) {
- v4l_info(&chip->c, "found tda9874%s.\n", (dic == 0x11) ? "a":"h");
+ v4l_info(chip->c, "found tda9874%s.\n", (dic == 0x11) ? "a":"h");
tda9874a_dic = dic; /* remember device id. */
return 1;
}
@@ -1095,7 +1094,7 @@ static int tda8425_initialize(struct CHIPSTATE *chip)
int inputmap[4] = { /* tuner */ TDA8425_S1_CH2, /* radio */ TDA8425_S1_CH1,
/* extern */ TDA8425_S1_CH1, /* intern */ TDA8425_S1_OFF};
- if (chip->c.adapter->id == I2C_HW_B_RIVA) {
+ if (chip->c->adapter->id == I2C_HW_B_RIVA) {
memcpy (desc->inputmap, inputmap, sizeof (inputmap));
}
return 0;
@@ -1105,20 +1104,20 @@ static void tda8425_setmode(struct CHIPSTATE *chip, int mode)
{
int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1;
- if (mode & VIDEO_SOUND_LANG1) {
+ if (mode & V4L2_TUNER_MODE_LANG1) {
s1 |= TDA8425_S1_ML_SOUND_A;
s1 |= TDA8425_S1_STEREO_PSEUDO;
- } else if (mode & VIDEO_SOUND_LANG2) {
+ } else if (mode & V4L2_TUNER_MODE_LANG2) {
s1 |= TDA8425_S1_ML_SOUND_B;
s1 |= TDA8425_S1_STEREO_PSEUDO;
} else {
s1 |= TDA8425_S1_ML_STEREO;
- if (mode & VIDEO_SOUND_MONO)
+ if (mode & V4L2_TUNER_MODE_MONO)
s1 |= TDA8425_S1_STEREO_MONO;
- if (mode & VIDEO_SOUND_STEREO)
+ if (mode & V4L2_TUNER_MODE_STEREO)
s1 |= TDA8425_S1_STEREO_SPATIAL;
}
chip_write(chip,TDA8425_S1,s1);
@@ -1177,13 +1176,13 @@ static int ta8874z_getmode(struct CHIPSTATE *chip)
int val, mode;
val = chip_read(chip);
- mode = VIDEO_SOUND_MONO;
+ mode = V4L2_TUNER_MODE_MONO;
if (val & TA8874Z_B1){
- mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+ mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
}else if (!(val & TA8874Z_B0)){
- mode |= VIDEO_SOUND_STEREO;
+ mode |= V4L2_TUNER_MODE_STEREO;
}
- /* v4l_dbg(1, debug, &chip->c, "ta8874z_getmode(): raw chip read: 0x%02x, return: 0x%02x\n", val, mode); */
+ /* v4l_dbg(1, debug, chip->c, "ta8874z_getmode(): raw chip read: 0x%02x, return: 0x%02x\n", val, mode); */
return mode;
}
@@ -1196,19 +1195,19 @@ static void ta8874z_setmode(struct CHIPSTATE *chip, int mode)
{
int update = 1;
audiocmd *t = NULL;
- v4l_dbg(1, debug, &chip->c, "ta8874z_setmode(): mode: 0x%02x\n", mode);
+ v4l_dbg(1, debug, chip->c, "ta8874z_setmode(): mode: 0x%02x\n", mode);
switch(mode){
- case VIDEO_SOUND_MONO:
+ case V4L2_TUNER_MODE_MONO:
t = &ta8874z_mono;
break;
- case VIDEO_SOUND_STEREO:
+ case V4L2_TUNER_MODE_STEREO:
t = &ta8874z_stereo;
break;
- case VIDEO_SOUND_LANG1:
+ case V4L2_TUNER_MODE_LANG1:
t = &ta8874z_main;
break;
- case VIDEO_SOUND_LANG2:
+ case V4L2_TUNER_MODE_LANG2:
t = &ta8874z_sub;
break;
default:
@@ -1462,51 +1461,55 @@ static struct CHIPDESC chiplist[] = {
/* ---------------------------------------------------------------------- */
/* i2c registration */
-static int chip_attach(struct i2c_adapter *adap, int addr, int kind)
+static int chip_probe(struct i2c_client *client)
{
struct CHIPSTATE *chip;
struct CHIPDESC *desc;
+ if (debug) {
+ printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
+ printk(KERN_INFO "tvaudio: known chips: ");
+ for (desc = chiplist; desc->name != NULL; desc++)
+ printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name);
+ printk("\n");
+ }
+
chip = kzalloc(sizeof(*chip),GFP_KERNEL);
if (!chip)
return -ENOMEM;
- memcpy(&chip->c,&client_template,sizeof(struct i2c_client));
- chip->c.adapter = adap;
- chip->c.addr = addr;
- i2c_set_clientdata(&chip->c, chip);
+ chip->c = client;
+ i2c_set_clientdata(client, chip);
/* find description for the chip */
- v4l_dbg(1, debug, &chip->c, "chip found @ 0x%x\n", addr<<1);
+ v4l_dbg(1, debug, client, "chip found @ 0x%x\n", client->addr<<1);
for (desc = chiplist; desc->name != NULL; desc++) {
if (0 == *(desc->insmodopt))
continue;
- if (addr < desc->addr_lo ||
- addr > desc->addr_hi)
+ if (client->addr < desc->addr_lo ||
+ client->addr > desc->addr_hi)
continue;
if (desc->checkit && !desc->checkit(chip))
continue;
break;
}
if (desc->name == NULL) {
- v4l_dbg(1, debug, &chip->c, "no matching chip description found\n");
+ v4l_dbg(1, debug, client, "no matching chip description found\n");
return -EIO;
}
- v4l_info(&chip->c, "%s found @ 0x%x (%s)\n", desc->name, addr<<1, adap->name);
+ v4l_info(client, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name);
if (desc->flags) {
- v4l_dbg(1, debug, &chip->c, "matches:%s%s%s.\n",
+ v4l_dbg(1, debug, client, "matches:%s%s%s.\n",
(desc->flags & CHIP_HAS_VOLUME) ? " volume" : "",
(desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
(desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : "");
}
/* fill required data structures */
- strcpy(chip->c.name, desc->name);
+ strcpy(client->name, desc->name);
chip->type = desc-chiplist;
chip->shadow.count = desc->registers+1;
chip->prevmode = -1;
chip->audmode = V4L2_TUNER_MODE_LANG1;
- /* register */
- i2c_attach_client(&chip->c);
/* initialization */
if (desc->initialize != NULL)
@@ -1533,28 +1536,17 @@ static int chip_attach(struct i2c_adapter *adap, int addr, int kind)
init_timer(&chip->wt);
chip->wt.function = chip_thread_wake;
chip->wt.data = (unsigned long)chip;
- chip->thread = kthread_run(chip_thread, chip, chip->c.name);
+ chip->thread = kthread_run(chip_thread, chip, chip->c->name);
if (IS_ERR(chip->thread)) {
- v4l_warn(&chip->c, "%s: failed to create kthread\n",
- chip->c.name);
+ v4l_warn(chip->c, "%s: failed to create kthread\n",
+ chip->c->name);
chip->thread = NULL;
}
}
return 0;
}
-static int chip_probe(struct i2c_adapter *adap)
-{
- /* don't attach on saa7146 based cards,
- because dedicated drivers are used */
- if ((adap->id == I2C_HW_SAA7146))
- return 0;
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, chip_attach);
- return 0;
-}
-
-static int chip_detach(struct i2c_client *client)
+static int chip_remove(struct i2c_client *client)
{
struct CHIPSTATE *chip = i2c_get_clientdata(client);
@@ -1565,12 +1557,52 @@ static int chip_detach(struct i2c_client *client)
chip->thread = NULL;
}
- i2c_detach_client(&chip->c);
kfree(chip);
return 0;
}
-static int tvaudio_set_ctrl(struct CHIPSTATE *chip, struct v4l2_control *ctrl)
+static int tvaudio_get_ctrl(struct CHIPSTATE *chip,
+ struct v4l2_control *ctrl)
+{
+ struct CHIPDESC *desc = chiplist + chip->type;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=chip->muted;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ if (!desc->flags & CHIP_HAS_VOLUME)
+ break;
+ ctrl->value = max(chip->left,chip->right);
+ return 0;
+ case V4L2_CID_AUDIO_BALANCE:
+ {
+ int volume;
+ if (!desc->flags & CHIP_HAS_VOLUME)
+ break;
+ volume = max(chip->left,chip->right);
+ if (volume)
+ ctrl->value=(32768*min(chip->left,chip->right))/volume;
+ else
+ ctrl->value=32768;
+ return 0;
+ }
+ case V4L2_CID_AUDIO_BASS:
+ if (desc->flags & CHIP_HAS_BASSTREBLE)
+ break;
+ ctrl->value = chip->bass;
+ return 0;
+ case V4L2_CID_AUDIO_TREBLE:
+ if (desc->flags & CHIP_HAS_BASSTREBLE)
+ return -EINVAL;
+ ctrl->value = chip->treble;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int tvaudio_set_ctrl(struct CHIPSTATE *chip,
+ struct v4l2_control *ctrl)
{
struct CHIPDESC *desc = chiplist + chip->type;
@@ -1584,11 +1616,60 @@ static int tvaudio_set_ctrl(struct CHIPSTATE *chip, struct v4l2_control *ctrl)
else
chip_write_masked(chip,desc->inputreg,
desc->inputmap[chip->input],desc->inputmask);
- break;
- default:
- return -EINVAL;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ {
+ int volume,balance;
+
+ if (!desc->flags & CHIP_HAS_VOLUME)
+ break;
+
+ volume = max(chip->left,chip->right);
+ if (volume)
+ balance=(32768*min(chip->left,chip->right))/volume;
+ else
+ balance=32768;
+
+ volume=ctrl->value;
+ chip->left = (min(65536 - balance,32768) * volume) / 32768;
+ chip->right = (min(balance,volume *(__u16)32768)) / 32768;
+
+ chip_write(chip,desc->leftreg,desc->volfunc(chip->left));
+ chip_write(chip,desc->rightreg,desc->volfunc(chip->right));
+
+ return 0;
}
- return 0;
+ case V4L2_CID_AUDIO_BALANCE:
+ {
+ int volume, balance;
+ if (!desc->flags & CHIP_HAS_VOLUME)
+ break;
+
+ volume = max(chip->left,chip->right);
+ balance = ctrl->value;
+
+ chip_write(chip,desc->leftreg,desc->volfunc(chip->left));
+ chip_write(chip,desc->rightreg,desc->volfunc(chip->right));
+
+ return 0;
+ }
+ case V4L2_CID_AUDIO_BASS:
+ if (desc->flags & CHIP_HAS_BASSTREBLE)
+ break;
+ chip->bass = ctrl->value;
+ chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass));
+
+ return 0;
+ case V4L2_CID_AUDIO_TREBLE:
+ if (desc->flags & CHIP_HAS_BASSTREBLE)
+ return -EINVAL;
+
+ chip->treble = ctrl->value;
+ chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble));
+
+ return 0;
+ }
+ return -EINVAL;
}
@@ -1601,7 +1682,7 @@ static int chip_command(struct i2c_client *client,
struct CHIPSTATE *chip = i2c_get_clientdata(client);
struct CHIPDESC *desc = chiplist + chip->type;
- v4l_dbg(1, debug, &chip->c, "%s: chip_command 0x%x\n", chip->c.name, cmd);
+ v4l_dbg(1, debug, chip->c, "%s: chip_command 0x%x\n", chip->c->name, cmd);
switch (cmd) {
case AUDC_SET_RADIO:
@@ -1609,67 +1690,36 @@ static int chip_command(struct i2c_client *client,
chip->watch_stereo = 0;
/* del_timer(&chip->wt); */
break;
-
/* --- v4l ioctls --- */
/* take care: bttv does userspace copying, we'll get a
kernel pointer here... */
- case VIDIOCGAUDIO:
- {
- struct video_audio *va = arg;
-
- if (desc->flags & CHIP_HAS_VOLUME) {
- va->flags |= VIDEO_AUDIO_VOLUME;
- va->volume = max(chip->left,chip->right);
- if (va->volume)
- va->balance = (32768*min(chip->left,chip->right))/
- va->volume;
- else
- va->balance = 32768;
- }
- if (desc->flags & CHIP_HAS_BASSTREBLE) {
- va->flags |= VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE;
- va->bass = chip->bass;
- va->treble = chip->treble;
- }
- if (!chip->radio) {
- if (desc->getmode)
- va->mode = desc->getmode(chip);
- else
- va->mode = VIDEO_SOUND_MONO;
- }
- break;
- }
-
- case VIDIOCSAUDIO:
+ case VIDIOC_QUERYCTRL:
{
- struct video_audio *va = arg;
-
- if (desc->flags & CHIP_HAS_VOLUME) {
- chip->left = (min(65536 - va->balance,32768) *
- va->volume) / 32768;
- chip->right = (min(va->balance,(__u16)32768) *
- va->volume) / 32768;
- chip_write(chip,desc->leftreg,desc->volfunc(chip->left));
- chip_write(chip,desc->rightreg,desc->volfunc(chip->right));
- }
- if (desc->flags & CHIP_HAS_BASSTREBLE) {
- chip->bass = va->bass;
- chip->treble = va->treble;
- chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass));
- chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble));
- }
- if (desc->setmode && va->mode) {
- chip->watch_stereo = 0;
- /* del_timer(&chip->wt); */
- chip->mode = va->mode;
- desc->setmode(chip,va->mode);
+ struct v4l2_queryctrl *qc = arg;
+
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ if (!desc->flags & CHIP_HAS_VOLUME)
+ return -EINVAL;
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ if (desc->flags & CHIP_HAS_BASSTREBLE)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
}
- break;
+ return v4l2_ctrl_query_fill_std(qc);
}
-
case VIDIOC_S_CTRL:
return tvaudio_set_ctrl(chip, arg);
+ case VIDIOC_G_CTRL:
+ return tvaudio_get_ctrl(chip, arg);
case VIDIOC_INT_G_AUDIO_ROUTING:
{
struct v4l2_routing *rt = arg;
@@ -1678,7 +1728,6 @@ static int chip_command(struct i2c_client *client,
rt->output = 0;
break;
}
-
case VIDIOC_INT_S_AUDIO_ROUTING:
{
struct v4l2_routing *rt = arg;
@@ -1693,7 +1742,6 @@ static int chip_command(struct i2c_client *client,
desc->inputmap[chip->input], desc->inputmask);
break;
}
-
case VIDIOC_S_TUNER:
{
struct v4l2_tuner *vt = arg;
@@ -1703,17 +1751,13 @@ static int chip_command(struct i2c_client *client,
break;
switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO:
- mode = VIDEO_SOUND_MONO;
- break;
case V4L2_TUNER_MODE_STEREO:
- case V4L2_TUNER_MODE_LANG1_LANG2:
- mode = VIDEO_SOUND_STEREO;
- break;
case V4L2_TUNER_MODE_LANG1:
- mode = VIDEO_SOUND_LANG1;
- break;
case V4L2_TUNER_MODE_LANG2:
- mode = VIDEO_SOUND_LANG2;
+ mode = vt->audmode;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ mode = V4L2_TUNER_MODE_STEREO;
break;
default:
return -EINVAL;
@@ -1728,11 +1772,10 @@ static int chip_command(struct i2c_client *client,
}
break;
}
-
case VIDIOC_G_TUNER:
{
struct v4l2_tuner *vt = arg;
- int mode = VIDEO_SOUND_MONO;
+ int mode = V4L2_TUNER_MODE_MONO;
if (chip->radio)
break;
@@ -1744,30 +1787,26 @@ static int chip_command(struct i2c_client *client,
if (desc->getmode)
mode = desc->getmode(chip);
- if (mode & VIDEO_SOUND_MONO)
+ if (mode & V4L2_TUNER_MODE_MONO)
vt->rxsubchans |= V4L2_TUNER_SUB_MONO;
- if (mode & VIDEO_SOUND_STEREO)
+ if (mode & V4L2_TUNER_MODE_STEREO)
vt->rxsubchans |= V4L2_TUNER_SUB_STEREO;
/* Note: for SAP it should be mono/lang2 or stereo/lang2.
When this module is converted fully to v4l2, then this
should change for those chips that can detect SAP. */
- if (mode & VIDEO_SOUND_LANG1)
+ if (mode & V4L2_TUNER_MODE_LANG1)
vt->rxsubchans = V4L2_TUNER_SUB_LANG1 |
V4L2_TUNER_SUB_LANG2;
break;
}
-
- case VIDIOCSCHAN:
case VIDIOC_S_STD:
chip->radio = 0;
break;
-
- case VIDIOCSFREQ:
case VIDIOC_S_FREQUENCY:
chip->mode = 0; /* automatic */
if (desc->checkmode) {
- desc->setmode(chip,VIDEO_SOUND_MONO);
- if (chip->prevmode != VIDEO_SOUND_MONO)
+ desc->setmode(chip,V4L2_TUNER_MODE_MONO);
+ if (chip->prevmode != V4L2_TUNER_MODE_MONO)
chip->prevmode = -1; /* reset previous mode */
mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
/* the thread will call checkmode() later */
@@ -1780,44 +1819,25 @@ static int chip_command(struct i2c_client *client,
return 0;
}
-static struct i2c_driver driver = {
- .driver = {
- .name = "tvaudio",
- },
- .id = I2C_DRIVERID_TVAUDIO,
- .attach_adapter = chip_probe,
- .detach_client = chip_detach,
- .command = chip_command,
-};
-
-static struct i2c_client client_template =
+static int chip_legacy_probe(struct i2c_adapter *adap)
{
- .name = "(unset)",
- .driver = &driver,
-};
-
-static int __init audiochip_init_module(void)
-{
- struct CHIPDESC *desc;
-
- if (debug) {
- printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
- printk(KERN_INFO "tvaudio: known chips: ");
- for (desc = chiplist; desc->name != NULL; desc++)
- printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name);
- printk("\n");
- }
-
- return i2c_add_driver(&driver);
-}
-
-static void __exit audiochip_cleanup_module(void)
-{
- i2c_del_driver(&driver);
+ /* don't attach on saa7146 based cards,
+ because dedicated drivers are used */
+ if ((adap->id == I2C_HW_SAA7146))
+ return 0;
+ if (adap->class & I2C_CLASS_TV_ANALOG)
+ return 1;
+ return 0;
}
-module_init(audiochip_init_module);
-module_exit(audiochip_cleanup_module);
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tvaudio",
+ .driverid = I2C_DRIVERID_TVAUDIO,
+ .command = chip_command,
+ .probe = chip_probe,
+ .remove = chip_remove,
+ .legacy_probe = chip_legacy_probe,
+};
/*
* Local variables:
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
index 4b2c4034f5b3..0b8fbad3c721 100644
--- a/drivers/media/video/tveeprom.c
+++ b/drivers/media/video/tveeprom.c
@@ -46,11 +46,12 @@ MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver");
MODULE_AUTHOR("John Klar");
MODULE_LICENSE("GPL");
-static int debug = 0;
+static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
-#define STRM(array,i) (i < sizeof(array)/sizeof(char*) ? array[i] : "unknown")
+#define STRM(array, i) \
+ (i < sizeof(array) / sizeof(char *) ? array[i] : "unknown")
#define tveeprom_info(fmt, arg...) \
v4l_printk(KERN_INFO, "tveeprom", c->adapter, c->addr, fmt , ## arg)
@@ -58,7 +59,8 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
v4l_printk(KERN_WARNING, "tveeprom", c->adapter, c->addr, fmt , ## arg)
#define tveeprom_dbg(fmt, arg...) do { \
if (debug) \
- v4l_printk(KERN_DEBUG, "tveeprom", c->adapter, c->addr, fmt , ## arg); \
+ v4l_printk(KERN_DEBUG, "tveeprom", \
+ c->adapter, c->addr, fmt , ## arg); \
} while (0)
/*
@@ -94,170 +96,172 @@ static struct HAUPPAUGE_TUNER
hauppauge_tuner[] =
{
/* 0-9 */
- { TUNER_ABSENT, "None" },
- { TUNER_ABSENT, "External" },
- { TUNER_ABSENT, "Unspecified" },
- { TUNER_PHILIPS_PAL, "Philips FI1216" },
- { TUNER_PHILIPS_SECAM, "Philips FI1216MF" },
- { TUNER_PHILIPS_NTSC, "Philips FI1236" },
- { TUNER_PHILIPS_PAL_I, "Philips FI1246" },
- { TUNER_PHILIPS_PAL_DK,"Philips FI1256" },
- { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" },
- { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" },
+ { TUNER_ABSENT, "None" },
+ { TUNER_ABSENT, "External" },
+ { TUNER_ABSENT, "Unspecified" },
+ { TUNER_PHILIPS_PAL, "Philips FI1216" },
+ { TUNER_PHILIPS_SECAM, "Philips FI1216MF" },
+ { TUNER_PHILIPS_NTSC, "Philips FI1236" },
+ { TUNER_PHILIPS_PAL_I, "Philips FI1246" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FI1256" },
+ { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" },
+ { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" },
/* 10-19 */
- { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" },
- { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" },
- { TUNER_PHILIPS_PAL_DK,"Philips FI1256 MK2" },
- { TUNER_TEMIC_NTSC, "Temic 4032FY5" },
- { TUNER_TEMIC_PAL, "Temic 4002FH5" },
- { TUNER_TEMIC_PAL_I, "Temic 4062FY5" },
- { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" },
- { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" },
- { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" },
- { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" },
+ { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" },
+ { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2" },
+ { TUNER_TEMIC_NTSC, "Temic 4032FY5" },
+ { TUNER_TEMIC_PAL, "Temic 4002FH5" },
+ { TUNER_TEMIC_PAL_I, "Temic 4062FY5" },
+ { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" },
+ { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" },
+ { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" },
+ { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" },
/* 20-29 */
- { TUNER_PHILIPS_PAL_DK,"Philips FR1256 MK2" },
- { TUNER_PHILIPS_PAL, "Philips FM1216" },
- { TUNER_PHILIPS_SECAM, "Philips FM1216MF" },
- { TUNER_PHILIPS_NTSC, "Philips FM1236" },
- { TUNER_PHILIPS_PAL_I, "Philips FM1246" },
- { TUNER_PHILIPS_PAL_DK,"Philips FM1256" },
- { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" },
- { TUNER_ABSENT, "Samsung TCPN9082D" },
- { TUNER_ABSENT, "Samsung TCPM9092P" },
- { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2" },
+ { TUNER_PHILIPS_PAL, "Philips FM1216" },
+ { TUNER_PHILIPS_SECAM, "Philips FM1216MF" },
+ { TUNER_PHILIPS_NTSC, "Philips FM1236" },
+ { TUNER_PHILIPS_PAL_I, "Philips FM1246" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FM1256" },
+ { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" },
+ { TUNER_ABSENT, "Samsung TCPN9082D" },
+ { TUNER_ABSENT, "Samsung TCPM9092P" },
+ { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" },
/* 30-39 */
- { TUNER_ABSENT, "Samsung TCPN9085D" },
- { TUNER_ABSENT, "Samsung TCPB9085P" },
- { TUNER_ABSENT, "Samsung TCPL9091P" },
- { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" },
- { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" },
- { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" },
- { TUNER_PHILIPS_NTSC, "Philips TD1536" },
- { TUNER_PHILIPS_NTSC, "Philips TD1536D" },
- { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */
- { TUNER_ABSENT, "Philips FI1256MP" },
+ { TUNER_ABSENT, "Samsung TCPN9085D" },
+ { TUNER_ABSENT, "Samsung TCPB9085P" },
+ { TUNER_ABSENT, "Samsung TCPL9091P" },
+ { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" },
+ { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" },
+ { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" },
+ { TUNER_PHILIPS_NTSC, "Philips TD1536" },
+ { TUNER_PHILIPS_NTSC, "Philips TD1536D" },
+ { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */
+ { TUNER_ABSENT, "Philips FI1256MP" },
/* 40-49 */
- { TUNER_ABSENT, "Samsung TCPQ9091P" },
+ { TUNER_ABSENT, "Samsung TCPQ9091P" },
{ TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5" },
- { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" },
- { TUNER_TEMIC_4046FM5, "Temic 4046FM5" },
+ { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" },
+ { TUNER_TEMIC_4046FM5, "Temic 4046FM5" },
{ TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" },
- { TUNER_ABSENT, "Philips TD1536D FH 44"},
- { TUNER_LG_NTSC_FM, "LG TP18NSR01F"},
- { TUNER_LG_PAL_FM, "LG TP18PSB01D"},
- { TUNER_LG_PAL, "LG TP18PSB11D"},
- { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"},
+ { TUNER_ABSENT, "Philips TD1536D FH 44"},
+ { TUNER_LG_NTSC_FM, "LG TP18NSR01F"},
+ { TUNER_LG_PAL_FM, "LG TP18PSB01D"},
+ { TUNER_LG_PAL, "LG TP18PSB11D"},
+ { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"},
/* 50-59 */
- { TUNER_LG_PAL_I, "LG TAPC-I701D"},
- { TUNER_ABSENT, "Temic 4042FI5"},
- { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"},
- { TUNER_ABSENT, "LG TPI8NSR11F"},
- { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"},
- { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"},
- { TUNER_ABSENT, "Philips FI1236 MK3"},
- { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"},
- { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"},
- { TUNER_ABSENT, "Philips FM1216MP MK3"},
+ { TUNER_LG_PAL_I, "LG TAPC-I701D"},
+ { TUNER_ABSENT, "Temic 4042FI5"},
+ { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"},
+ { TUNER_ABSENT, "LG TPI8NSR11F"},
+ { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"},
+ { TUNER_ABSENT, "Philips FI1236 MK3"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"},
+ { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"},
+ { TUNER_ABSENT, "Philips FM1216MP MK3"},
/* 60-69 */
- { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"},
- { TUNER_ABSENT, "LG M001D MK3"},
- { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"},
- { TUNER_ABSENT, "LG M701D MK3"},
- { TUNER_ABSENT, "Temic 4146FM5"},
- { TUNER_ABSENT, "Temic 4136FY5"},
- { TUNER_ABSENT, "Temic 4106FH5"},
- { TUNER_ABSENT, "Philips FQ1216LMP MK3"},
- { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"},
- { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"},
+ { TUNER_ABSENT, "LG M001D MK3"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"},
+ { TUNER_ABSENT, "LG M701D MK3"},
+ { TUNER_ABSENT, "Temic 4146FM5"},
+ { TUNER_ABSENT, "Temic 4136FY5"},
+ { TUNER_ABSENT, "Temic 4106FH5"},
+ { TUNER_ABSENT, "Philips FQ1216LMP MK3"},
+ { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"},
+ { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"},
/* 70-79 */
- { TUNER_ABSENT, "LG TALN H200T"},
- { TUNER_ABSENT, "LG TALN H250T"},
- { TUNER_ABSENT, "LG TALN M200T"},
- { TUNER_ABSENT, "LG TALN Z200T"},
- { TUNER_ABSENT, "LG TALN S200T"},
- { TUNER_ABSENT, "Thompson DTT7595"},
- { TUNER_ABSENT, "Thompson DTT7592"},
- { TUNER_ABSENT, "Silicon TDA8275C1 8290"},
- { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"},
- { TUNER_ABSENT, "Thompson DTT757"},
+ { TUNER_ABSENT, "LG TALN H200T"},
+ { TUNER_ABSENT, "LG TALN H250T"},
+ { TUNER_ABSENT, "LG TALN M200T"},
+ { TUNER_ABSENT, "LG TALN Z200T"},
+ { TUNER_ABSENT, "LG TALN S200T"},
+ { TUNER_ABSENT, "Thompson DTT7595"},
+ { TUNER_ABSENT, "Thompson DTT7592"},
+ { TUNER_ABSENT, "Silicon TDA8275C1 8290"},
+ { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"},
+ { TUNER_ABSENT, "Thompson DTT757"},
/* 80-89 */
- { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216LME MK3"},
- { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"},
- { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"},
- { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"},
- { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"},
- { TUNER_TCL_2002N, "TCL 2002N 6A"},
- { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"},
- { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"},
- { TUNER_ABSENT, "Samsung TCPE 4121P30A"},
- { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216LME MK3"},
+ { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"},
+ { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"},
+ { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"},
+ { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"},
+ { TUNER_TCL_2002N, "TCL 2002N 6A"},
+ { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"},
+ { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"},
+ { TUNER_ABSENT, "Samsung TCPE 4121P30A"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"},
/* 90-99 */
- { TUNER_ABSENT, "LG TALN H202T"},
- { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"},
- { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"},
- { TUNER_ABSENT, "Philips FQ1286A MK4"},
- { TUNER_ABSENT, "Philips FQ1216ME MK5"},
- { TUNER_ABSENT, "Philips FQ1236 MK5"},
- { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"},
- { TUNER_TCL_2002MB, "TCL 2002MB_3H"},
- { TUNER_ABSENT, "TCL 2002MI_3H"},
- { TUNER_TCL_2002N, "TCL 2002N 5H"},
+ { TUNER_ABSENT, "LG TALN H202T"},
+ { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"},
+ { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"},
+ { TUNER_ABSENT, "Philips FQ1286A MK4"},
+ { TUNER_ABSENT, "Philips FQ1216ME MK5"},
+ { TUNER_ABSENT, "Philips FQ1236 MK5"},
+ { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"},
+ { TUNER_TCL_2002MB, "TCL 2002MB_3H"},
+ { TUNER_ABSENT, "TCL 2002MI_3H"},
+ { TUNER_TCL_2002N, "TCL 2002N 5H"},
/* 100-109 */
- { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"},
- { TUNER_TEA5767, "Philips TEA5768HL FM Radio"},
- { TUNER_ABSENT, "Panasonic ENV57H12D5"},
- { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"},
- { TUNER_ABSENT, "TCL MNM05-4"},
- { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"},
- { TUNER_ABSENT, "TCL MQNM05-4"},
- { TUNER_ABSENT, "LG TAPC-W701D"},
- { TUNER_ABSENT, "TCL 9886P-WM"},
- { TUNER_ABSENT, "TCL 1676NM-WM"},
+ { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"},
+ { TUNER_TEA5767, "Philips TEA5768HL FM Radio"},
+ { TUNER_ABSENT, "Panasonic ENV57H12D5"},
+ { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"},
+ { TUNER_ABSENT, "TCL MNM05-4"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"},
+ { TUNER_ABSENT, "TCL MQNM05-4"},
+ { TUNER_ABSENT, "LG TAPC-W701D"},
+ { TUNER_ABSENT, "TCL 9886P-WM"},
+ { TUNER_ABSENT, "TCL 1676NM-WM"},
/* 110-119 */
- { TUNER_ABSENT, "Thompson DTT75105"},
- { TUNER_ABSENT, "Conexant_CX24109"},
- { TUNER_TCL_2002N, "TCL M2523_5N_E"},
- { TUNER_TCL_2002MB, "TCL M2523_3DB_E"},
- { TUNER_ABSENT, "Philips 8275A"},
- { TUNER_ABSENT, "Microtune MT2060"},
- { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"},
- { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"},
- { TUNER_ABSENT, "TCL M2523_3DI_E"},
- { TUNER_ABSENT, "Samsung THPD5222FG30A"},
+ { TUNER_ABSENT, "Thompson DTT75105"},
+ { TUNER_ABSENT, "Conexant_CX24109"},
+ { TUNER_TCL_2002N, "TCL M2523_5N_E"},
+ { TUNER_TCL_2002MB, "TCL M2523_3DB_E"},
+ { TUNER_ABSENT, "Philips 8275A"},
+ { TUNER_ABSENT, "Microtune MT2060"},
+ { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"},
+ { TUNER_ABSENT, "TCL M2523_3DI_E"},
+ { TUNER_ABSENT, "Samsung THPD5222FG30A"},
/* 120-129 */
- { TUNER_ABSENT, "Xceive XC3028"},
- { TUNER_ABSENT, "Philips FQ1216LME MK5"},
- { TUNER_ABSENT, "Philips FQD1216LME"},
- { TUNER_ABSENT, "Conexant CX24118A"},
- { TUNER_ABSENT, "TCL DMF11WIP"},
- { TUNER_ABSENT, "TCL MFNM05_4H_E"},
- { TUNER_ABSENT, "TCL MNM05_4H_E"},
- { TUNER_ABSENT, "TCL MPE05_2H_E"},
- { TUNER_ABSENT, "TCL MQNM05_4_U"},
- { TUNER_ABSENT, "TCL M2523_5NH_E"},
+ { TUNER_XC2028, "Xceive XC3028"},
+ { TUNER_ABSENT, "Philips FQ1216LME MK5"},
+ { TUNER_ABSENT, "Philips FQD1216LME"},
+ { TUNER_ABSENT, "Conexant CX24118A"},
+ { TUNER_ABSENT, "TCL DMF11WIP"},
+ { TUNER_ABSENT, "TCL MFNM05_4H_E"},
+ { TUNER_ABSENT, "TCL MNM05_4H_E"},
+ { TUNER_ABSENT, "TCL MPE05_2H_E"},
+ { TUNER_ABSENT, "TCL MQNM05_4_U"},
+ { TUNER_ABSENT, "TCL M2523_5NH_E"},
/* 130-139 */
- { TUNER_ABSENT, "TCL M2523_3DBH_E"},
- { TUNER_ABSENT, "TCL M2523_3DIH_E"},
- { TUNER_ABSENT, "TCL MFPE05_2_U"},
- { TUNER_ABSENT, "Philips FMD1216MEX"},
- { TUNER_ABSENT, "Philips FRH2036B"},
- { TUNER_ABSENT, "Panasonic ENGF75_01GF"},
- { TUNER_ABSENT, "MaxLinear MXL5005"},
- { TUNER_ABSENT, "MaxLinear MXL5003"},
- { TUNER_ABSENT, "Xceive XC2028"},
- { TUNER_ABSENT, "Microtune MT2131"},
+ { TUNER_ABSENT, "TCL M2523_3DBH_E"},
+ { TUNER_ABSENT, "TCL M2523_3DIH_E"},
+ { TUNER_ABSENT, "TCL MFPE05_2_U"},
+ { TUNER_ABSENT, "Philips FMD1216MEX"},
+ { TUNER_ABSENT, "Philips FRH2036B"},
+ { TUNER_ABSENT, "Panasonic ENGF75_01GF"},
+ { TUNER_ABSENT, "MaxLinear MXL5005"},
+ { TUNER_ABSENT, "MaxLinear MXL5003"},
+ { TUNER_ABSENT, "Xceive XC2028"},
+ { TUNER_ABSENT, "Microtune MT2131"},
/* 140-149 */
- { TUNER_ABSENT, "Philips 8275A_8295"},
- { TUNER_ABSENT, "TCL MF02GIP_5N_E"},
- { TUNER_ABSENT, "TCL MF02GIP_3DB_E"},
- { TUNER_ABSENT, "TCL MF02GIP_3DI_E"},
- { TUNER_ABSENT, "Microtune MT2266"},
- { TUNER_ABSENT, "TCL MF10WPP_4N_E"},
- { TUNER_ABSENT, "LG TAPQ_H702F"},
- { TUNER_ABSENT, "TCL M09WPP_4N_E"},
- { TUNER_ABSENT, "MaxLinear MXL5005_v2"},
- { TUNER_ABSENT, "Philips 18271_8295"},
+ { TUNER_ABSENT, "Philips 8275A_8295"},
+ { TUNER_ABSENT, "TCL MF02GIP_5N_E"},
+ { TUNER_ABSENT, "TCL MF02GIP_3DB_E"},
+ { TUNER_ABSENT, "TCL MF02GIP_3DI_E"},
+ { TUNER_ABSENT, "Microtune MT2266"},
+ { TUNER_ABSENT, "TCL MF10WPP_4N_E"},
+ { TUNER_ABSENT, "LG TAPQ_H702F"},
+ { TUNER_ABSENT, "TCL M09WPP_4N_E"},
+ { TUNER_ABSENT, "MaxLinear MXL5005_v2"},
+ { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"},
+ /* 150-159 */
+ { TUNER_ABSENT, "Xceive XC5000"},
};
static struct HAUPPAUGE_AUDIOIC
@@ -344,37 +348,37 @@ static const char *decoderIC[] = {
static int hasRadioTuner(int tunerType)
{
switch (tunerType) {
- case 18: //PNPEnv_TUNER_FR1236_MK2:
- case 23: //PNPEnv_TUNER_FM1236:
- case 38: //PNPEnv_TUNER_FMR1236:
- case 16: //PNPEnv_TUNER_FR1216_MK2:
- case 19: //PNPEnv_TUNER_FR1246_MK2:
- case 21: //PNPEnv_TUNER_FM1216:
- case 24: //PNPEnv_TUNER_FM1246:
- case 17: //PNPEnv_TUNER_FR1216MF_MK2:
- case 22: //PNPEnv_TUNER_FM1216MF:
- case 20: //PNPEnv_TUNER_FR1256_MK2:
- case 25: //PNPEnv_TUNER_FM1256:
- case 33: //PNPEnv_TUNER_4039FR5:
- case 42: //PNPEnv_TUNER_4009FR5:
- case 52: //PNPEnv_TUNER_4049FM5:
- case 54: //PNPEnv_TUNER_4049FM5_AltI2C:
- case 44: //PNPEnv_TUNER_4009FN5:
- case 31: //PNPEnv_TUNER_TCPB9085P:
- case 30: //PNPEnv_TUNER_TCPN9085D:
- case 46: //PNPEnv_TUNER_TP18NSR01F:
- case 47: //PNPEnv_TUNER_TP18PSB01D:
- case 49: //PNPEnv_TUNER_TAPC_I001D:
- case 60: //PNPEnv_TUNER_TAPE_S001D_MK3:
- case 57: //PNPEnv_TUNER_FM1216ME_MK3:
- case 59: //PNPEnv_TUNER_FM1216MP_MK3:
- case 58: //PNPEnv_TUNER_FM1236_MK3:
- case 68: //PNPEnv_TUNER_TAPE_H001F_MK3:
- case 61: //PNPEnv_TUNER_TAPE_M001D_MK3:
- case 78: //PNPEnv_TUNER_TDA8275C1_8290_FM:
- case 89: //PNPEnv_TUNER_TCL_MFPE05_2:
- case 92: //PNPEnv_TUNER_PHILIPS_FQ1236A_MK4:
- case 105:
+ case 18: /* PNPEnv_TUNER_FR1236_MK2 */
+ case 23: /* PNPEnv_TUNER_FM1236 */
+ case 38: /* PNPEnv_TUNER_FMR1236 */
+ case 16: /* PNPEnv_TUNER_FR1216_MK2 */
+ case 19: /* PNPEnv_TUNER_FR1246_MK2 */
+ case 21: /* PNPEnv_TUNER_FM1216 */
+ case 24: /* PNPEnv_TUNER_FM1246 */
+ case 17: /* PNPEnv_TUNER_FR1216MF_MK2 */
+ case 22: /* PNPEnv_TUNER_FM1216MF */
+ case 20: /* PNPEnv_TUNER_FR1256_MK2 */
+ case 25: /* PNPEnv_TUNER_FM1256 */
+ case 33: /* PNPEnv_TUNER_4039FR5 */
+ case 42: /* PNPEnv_TUNER_4009FR5 */
+ case 52: /* PNPEnv_TUNER_4049FM5 */
+ case 54: /* PNPEnv_TUNER_4049FM5_AltI2C */
+ case 44: /* PNPEnv_TUNER_4009FN5 */
+ case 31: /* PNPEnv_TUNER_TCPB9085P */
+ case 30: /* PNPEnv_TUNER_TCPN9085D */
+ case 46: /* PNPEnv_TUNER_TP18NSR01F */
+ case 47: /* PNPEnv_TUNER_TP18PSB01D */
+ case 49: /* PNPEnv_TUNER_TAPC_I001D */
+ case 60: /* PNPEnv_TUNER_TAPE_S001D_MK3 */
+ case 57: /* PNPEnv_TUNER_FM1216ME_MK3 */
+ case 59: /* PNPEnv_TUNER_FM1216MP_MK3 */
+ case 58: /* PNPEnv_TUNER_FM1236_MK3 */
+ case 68: /* PNPEnv_TUNER_TAPE_H001F_MK3 */
+ case 61: /* PNPEnv_TUNER_TAPE_M001D_MK3 */
+ case 78: /* PNPEnv_TUNER_TDA8275C1_8290_FM */
+ case 89: /* PNPEnv_TUNER_TCL_MFPE05_2 */
+ case 92: /* PNPEnv_TUNER_PHILIPS_FQ1236A_MK4 */
+ case 105:
return 1;
}
return 0;
@@ -392,7 +396,8 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
**
** In our (ivtv) case we're interested in the following:
** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuner)
- ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into hauppauge_tuner_fmt)
+ ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into
+ ** hauppauge_tuner_fmt)
** radio: tag [00].{last} or [0e].00 (bitmask. bit2=FM)
** audio proc: tag [02].01 or [05].00 (mask with 0x7f)
** decoder proc: tag [09].01)
@@ -405,9 +410,9 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
** # of inputs/outputs ???
*/
- int i, j, len, done, beenhere, tag,start;
+ int i, j, len, done, beenhere, tag, start;
- int tuner1 = 0, t_format1 = 0, audioic=-1;
+ int tuner1 = 0, t_format1 = 0, audioic = -1;
char *t_name1 = NULL;
const char *t_fmt_name1[8] = { " none", "", "", "", "", "", "", "" };
@@ -418,17 +423,24 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
memset(tvee, 0, sizeof(*tvee));
done = len = beenhere = 0;
- /* Hack for processing eeprom for em28xx and cx 2388x*/
- if ((eeprom_data[0] == 0x1a) && (eeprom_data[1] == 0xeb) &&
- (eeprom_data[2] == 0x67) && (eeprom_data[3] == 0x95))
- start=0xa0; /* Generic em28xx offset */
- else if (((eeprom_data[0] & 0xe1) == 0x01) &&
- (eeprom_data[1] == 0x00) &&
- (eeprom_data[2] == 0x00) &&
- (eeprom_data[8] == 0x84))
- start=8; /* Generic cx2388x offset */
+ /* Different eeprom start offsets for em28xx, cx2388x and cx23418 */
+ if (eeprom_data[0] == 0x1a &&
+ eeprom_data[1] == 0xeb &&
+ eeprom_data[2] == 0x67 &&
+ eeprom_data[3] == 0x95)
+ start = 0xa0; /* Generic em28xx offset */
+ else if ((eeprom_data[0] & 0xe1) == 0x01 &&
+ eeprom_data[1] == 0x00 &&
+ eeprom_data[2] == 0x00 &&
+ eeprom_data[8] == 0x84)
+ start = 8; /* Generic cx2388x offset */
+ else if (eeprom_data[1] == 0x70 &&
+ eeprom_data[2] == 0x00 &&
+ eeprom_data[4] == 0x74 &&
+ eeprom_data[8] == 0x84)
+ start = 8; /* Generic cx23418 offset (models 74xxx) */
else
- start=0;
+ start = 0;
for (i = start; !done && i < 256; i += len) {
if (eeprom_data[i] == 0x84) {
@@ -444,16 +456,17 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
++i;
} else {
tveeprom_warn("Encountered bad packet header [%02x]. "
- "Corrupt or not a Hauppauge eeprom.\n", eeprom_data[i]);
+ "Corrupt or not a Hauppauge eeprom.\n",
+ eeprom_data[i]);
return;
}
if (debug) {
- tveeprom_info("Tag [%02x] + %d bytes:", eeprom_data[i], len - 1);
- for(j = 1; j < len; j++) {
- printk(" %02x", eeprom_data[i + j]);
- }
- printk("\n");
+ tveeprom_info("Tag [%02x] + %d bytes:",
+ eeprom_data[i], len - 1);
+ for (j = 1; j < len; j++)
+ printk(KERN_CONT " %02x", eeprom_data[i + j]);
+ printk(KERN_CONT "\n");
}
/* process by tag */
@@ -504,16 +517,16 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
(eeprom_data[i+6] << 8) +
(eeprom_data[i+7] << 16);
- if ( (eeprom_data[i + 8] & 0xf0) &&
- (tvee->serial_number < 0xffffff) ) {
- tvee->MAC_address[0] = 0x00;
- tvee->MAC_address[1] = 0x0D;
- tvee->MAC_address[2] = 0xFE;
- tvee->MAC_address[3] = eeprom_data[i + 7];
- tvee->MAC_address[4] = eeprom_data[i + 6];
- tvee->MAC_address[5] = eeprom_data[i + 5];
- tvee->has_MAC_address = 1;
- }
+ if ((eeprom_data[i + 8] & 0xf0) &&
+ (tvee->serial_number < 0xffffff)) {
+ tvee->MAC_address[0] = 0x00;
+ tvee->MAC_address[1] = 0x0D;
+ tvee->MAC_address[2] = 0xFE;
+ tvee->MAC_address[3] = eeprom_data[i + 7];
+ tvee->MAC_address[4] = eeprom_data[i + 6];
+ tvee->MAC_address[5] = eeprom_data[i + 5];
+ tvee->has_MAC_address = 1;
+ }
break;
case 0x05:
@@ -537,7 +550,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
(eeprom_data[i + 3] << 16) +
(eeprom_data[i + 4] << 24);
tvee->revision =
- eeprom_data[i +5 ] +
+ eeprom_data[i + 5] +
(eeprom_data[i + 6] << 8) +
(eeprom_data[i + 7] << 16);
break;
@@ -557,16 +570,16 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
case 0x0a:
/* tag 'Tuner' */
if (beenhere == 0) {
- tuner1 = eeprom_data[i+2];
- t_format1 = eeprom_data[i+1];
+ tuner1 = eeprom_data[i + 2];
+ t_format1 = eeprom_data[i + 1];
beenhere = 1;
} else {
/* a second (radio) tuner may be present */
- tuner2 = eeprom_data[i+2];
- t_format2 = eeprom_data[i+1];
- if (t_format2 == 0) { /* not a TV tuner? */
+ tuner2 = eeprom_data[i + 2];
+ t_format2 = eeprom_data[i + 1];
+ /* not a TV tuner? */
+ if (t_format2 == 0)
tvee->has_radio = 1; /* must be radio */
- }
}
break;
@@ -594,7 +607,8 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
/* case 0x12: tag 'InfoBits' */
default:
- tveeprom_dbg("Not sure what to do with tag [%02x]\n", tag);
+ tveeprom_dbg("Not sure what to do with tag [%02x]\n",
+ tag);
/* dump the rest of the packet? */
}
}
@@ -608,7 +622,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f);
tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f);
tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f);
- tvee->rev_str[3] = 32 + ( tvee->revision & 0x3f);
+ tvee->rev_str[3] = 32 + (tvee->revision & 0x3f);
tvee->rev_str[4] = 0;
}
@@ -651,44 +665,40 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n",
tvee->model, tvee->rev_str, tvee->serial_number);
- if (tvee->has_MAC_address == 1) {
+ if (tvee->has_MAC_address == 1)
tveeprom_info("MAC address is %02X-%02X-%02X-%02X-%02X-%02X\n",
tvee->MAC_address[0], tvee->MAC_address[1],
tvee->MAC_address[2], tvee->MAC_address[3],
tvee->MAC_address[4], tvee->MAC_address[5]);
- }
tveeprom_info("tuner model is %s (idx %d, type %d)\n",
t_name1, tuner1, tvee->tuner_type);
tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n",
- t_fmt_name1[0], t_fmt_name1[1], t_fmt_name1[2], t_fmt_name1[3],
- t_fmt_name1[4], t_fmt_name1[5], t_fmt_name1[6], t_fmt_name1[7],
- t_format1);
- if (tuner2) {
+ t_fmt_name1[0], t_fmt_name1[1], t_fmt_name1[2],
+ t_fmt_name1[3], t_fmt_name1[4], t_fmt_name1[5],
+ t_fmt_name1[6], t_fmt_name1[7], t_format1);
+ if (tuner2)
tveeprom_info("second tuner model is %s (idx %d, type %d)\n",
t_name2, tuner2, tvee->tuner2_type);
- }
- if (t_format2) {
+ if (t_format2)
tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n",
- t_fmt_name2[0], t_fmt_name2[1], t_fmt_name2[2], t_fmt_name2[3],
- t_fmt_name2[4], t_fmt_name2[5], t_fmt_name2[6], t_fmt_name2[7],
- t_format2);
- }
- if (audioic<0) {
+ t_fmt_name2[0], t_fmt_name2[1], t_fmt_name2[2],
+ t_fmt_name2[3], t_fmt_name2[4], t_fmt_name2[5],
+ t_fmt_name2[6], t_fmt_name2[7], t_format2);
+ if (audioic < 0) {
tveeprom_info("audio processor is unknown (no idx)\n");
- tvee->audio_processor=AUDIO_CHIP_UNKNOWN;
+ tvee->audio_processor = AUDIO_CHIP_UNKNOWN;
} else {
if (audioic < ARRAY_SIZE(audioIC))
tveeprom_info("audio processor is %s (idx %d)\n",
- audioIC[audioic].name,audioic);
+ audioIC[audioic].name, audioic);
else
tveeprom_info("audio processor is unknown (idx %d)\n",
audioic);
}
- if (tvee->decoder_processor) {
+ if (tvee->decoder_processor)
tveeprom_info("decoder processor is %s (idx %d)\n",
STRM(decoderIC, tvee->decoder_processor),
tvee->decoder_processor);
- }
if (tvee->has_ir == -1)
tveeprom_info("has %sradio\n",
tvee->has_radio ? "" : "no ");
@@ -709,11 +719,13 @@ int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len)
int err;
buf = 0;
- if (1 != (err = i2c_master_send(c, &buf, 1))) {
+ err = i2c_master_send(c, &buf, 1);
+ if (err != 1) {
tveeprom_info("Huh, no eeprom present (err=%d)?\n", err);
return -1;
}
- if (len != (err = i2c_master_recv(c, eedata, len))) {
+ err = i2c_master_recv(c, eedata, len);
+ if (err != len) {
tveeprom_warn("i2c eeprom read error (err=%d)\n", err);
return -1;
}
@@ -724,9 +736,9 @@ int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len)
for (i = 0; i < len; i++) {
if (0 == (i % 16))
tveeprom_info("%02x:", i);
- printk(" %02x", eedata[i]);
+ printk(KERN_CONT " %02x", eedata[i]);
if (15 == (i % 16))
- printk("\n");
+ printk(KERN_CONT "\n");
}
}
return 0;
@@ -758,9 +770,9 @@ tveeprom_command(struct i2c_client *client,
switch (cmd) {
case 0:
- buf = kzalloc(256,GFP_KERNEL);
- tveeprom_read(client,buf,256);
- tveeprom_hauppauge_analog(client, &eeprom,buf);
+ buf = kzalloc(256, GFP_KERNEL);
+ tveeprom_read(client, buf, 256);
+ tveeprom_hauppauge_analog(client, &eeprom, buf);
kfree(buf);
eeprom_props[0] = eeprom.tuner_type;
eeprom_props[1] = eeprom.tuner_formats;
@@ -794,7 +806,7 @@ tveeprom_detect_client(struct i2c_adapter *adapter,
}
static int
-tveeprom_attach_adapter (struct i2c_adapter *adapter)
+tveeprom_attach_adapter(struct i2c_adapter *adapter)
{
if (adapter->class & I2C_CLASS_TV_ANALOG)
return i2c_probe(adapter, &addr_data, tveeprom_detect_client);
@@ -802,7 +814,7 @@ tveeprom_attach_adapter (struct i2c_adapter *adapter)
}
static int
-tveeprom_detach_client (struct i2c_client *client)
+tveeprom_detach_client(struct i2c_client *client)
{
int err;
diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c
index 0b2a961efd22..bd201397a2ac 100644
--- a/drivers/media/video/upd64031a.c
+++ b/drivers/media/video/upd64031a.c
@@ -28,30 +28,27 @@
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv.h>
#include <media/upd64031a.h>
-// --------------------- read registers functions define -----------------------
+/* --------------------- read registers functions define -------------------- */
/* bit masks */
#define GR_MODE_MASK 0xc0
#define DIRECT_3DYCS_CONNECT_MASK 0xc0
#define SYNC_CIRCUIT_MASK 0xa0
-// -----------------------------------------------------------------------------
+/* -------------------------------------------------------------------------- */
MODULE_DESCRIPTION("uPD64031A driver");
MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil");
MODULE_LICENSE("GPL");
-static int debug = 0;
+static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
-static unsigned short normal_i2c[] = { 0x24 >> 1, 0x26 >> 1, I2C_CLIENT_END };
-
-
-I2C_CLIENT_INSMOD;
enum {
R00 = 0, R01, R02, R03, R04,
@@ -99,7 +96,7 @@ static void upd64031a_write(struct i2c_client *client, u8 reg, u8 val)
buf[0] = reg;
buf[1] = val;
- v4l_dbg(1, debug, client, "writing reg addr: %02X val: %02X\n", reg, val);
+ v4l_dbg(1, debug, client, "write reg: %02X val: %02X\n", reg, val);
if (i2c_master_send(client, buf, 2) != 2)
v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val);
}
@@ -119,7 +116,7 @@ static void upd64031a_change(struct i2c_client *client)
/* ------------------------------------------------------------------------ */
-static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int upd64031a_command(struct i2c_client *client, unsigned cmd, void *arg)
{
struct upd64031a_state *state = i2c_get_clientdata(client);
struct v4l2_routing *route = arg;
@@ -143,8 +140,10 @@ static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void *
state->gr_mode = (route->input & 3) << 6;
state->direct_3dycs_connect = (route->input & 0xc) << 4;
- state->ext_comp_sync = (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1;
- state->ext_vert_sync = (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2;
+ state->ext_comp_sync =
+ (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1;
+ state->ext_vert_sync =
+ (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2;
r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode;
r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) |
state->ext_comp_sync | state->ext_vert_sync;
@@ -168,20 +167,23 @@ static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void *
{
struct v4l2_register *reg = arg;
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
+ if (cmd == VIDIOC_DBG_G_REGISTER) {
reg->val = upd64031a_read(client, reg->reg & 0xff);
- else
- upd64031a_write(client, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ }
+ upd64031a_write(client, reg->reg & 0xff, reg->val & 0xff);
break;
}
#endif
case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_UPD64031A, 0);
+ return v4l2_chip_ident_i2c_client(client, arg,
+ V4L2_IDENT_UPD64031A, 0);
default:
break;
@@ -193,90 +195,43 @@ static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void *
/* i2c implementation */
-static struct i2c_driver i2c_driver;
-
-static int upd64031a_attach(struct i2c_adapter *adapter, int address, int kind)
+static int upd64031a_probe(struct i2c_client *client)
{
- struct i2c_client *client;
struct upd64031a_state *state;
int i;
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return 0;
-
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == NULL) {
- return -ENOMEM;
- }
-
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver;
- snprintf(client->name, sizeof(client->name) - 1, "uPD64031A");
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
- v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name);
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL);
- if (state == NULL) {
- kfree(client);
+ if (state == NULL)
return -ENOMEM;
- }
i2c_set_clientdata(client, state);
memcpy(state->regs, upd64031a_init, sizeof(state->regs));
state->gr_mode = UPD64031A_GR_ON << 6;
state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4;
state->ext_comp_sync = state->ext_vert_sync = 0;
- for (i = 0; i < TOT_REGS; i++) {
+ for (i = 0; i < TOT_REGS; i++)
upd64031a_write(client, i, state->regs[i]);
- }
-
- i2c_attach_client(client);
-
return 0;
}
-static int upd64031a_probe(struct i2c_adapter *adapter)
+static int upd64031a_remove(struct i2c_client *client)
{
- if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, upd64031a_attach);
- return 0;
-}
-
-static int upd64031a_detach(struct i2c_client *client)
-{
- int err;
-
- err = i2c_detach_client(client);
- if (err)
- return err;
-
- kfree(client);
+ kfree(i2c_get_clientdata(client));
return 0;
}
/* ----------------------------------------------------------------------- */
-/* i2c implementation */
-static struct i2c_driver i2c_driver = {
- .driver = {
- .name = "upd64031a",
- },
- .id = I2C_DRIVERID_UPD64031A,
- .attach_adapter = upd64031a_probe,
- .detach_client = upd64031a_detach,
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "upd64031a",
+ .driverid = I2C_DRIVERID_UPD64031A,
.command = upd64031a_command,
+ .probe = upd64031a_probe,
+ .remove = upd64031a_remove,
};
-
-
-static int __init upd64031a_init_module(void)
-{
- return i2c_add_driver(&i2c_driver);
-}
-
-static void __exit upd64031a_exit_module(void)
-{
- i2c_del_driver(&i2c_driver);
-}
-
-module_init(upd64031a_init_module);
-module_exit(upd64031a_exit_module);
diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c
index 401bd21f46eb..2d9a88f70c85 100644
--- a/drivers/media/video/upd64083.c
+++ b/drivers/media/video/upd64083.c
@@ -17,7 +17,8 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
*/
#include <linux/version.h>
@@ -27,21 +28,18 @@
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv.h>
#include <media/upd64083.h>
MODULE_DESCRIPTION("uPD64083 driver");
MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil");
MODULE_LICENSE("GPL");
-static int debug = 0;
+static int debug;
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
-static unsigned short normal_i2c[] = { 0xb8 >> 1, 0xba >> 1, I2C_CLIENT_END };
-
-
-I2C_CLIENT_INSMOD;
enum {
R00 = 0, R01, R02, R03, R04,
@@ -88,7 +86,7 @@ static void upd64083_write(struct i2c_client *client, u8 reg, u8 val)
buf[0] = reg;
buf[1] = val;
- v4l_dbg(1, debug, client, "writing reg addr: %02x val: %02x\n", reg, val);
+ v4l_dbg(1, debug, client, "write reg: %02x val: %02x\n", reg, val);
if (i2c_master_send(client, buf, 2) != 2)
v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val);
}
@@ -109,7 +107,7 @@ static u8 upd64083_read(struct i2c_client *client, u8 reg)
/* ------------------------------------------------------------------------ */
-static int upd64083_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg)
{
struct upd64083_state *state = i2c_get_clientdata(client);
struct v4l2_routing *route = arg;
@@ -145,20 +143,23 @@ static int upd64083_command(struct i2c_client *client, unsigned int cmd, void *a
{
struct v4l2_register *reg = arg;
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
+ if (cmd == VIDIOC_DBG_G_REGISTER) {
reg->val = upd64083_read(client, reg->reg & 0xff);
- else
- upd64083_write(client, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ }
+ upd64083_write(client, reg->reg & 0xff, reg->val & 0xff);
break;
}
#endif
case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_UPD64083, 0);
+ return v4l2_chip_ident_i2c_client(client, arg,
+ V4L2_IDENT_UPD64083, 0);
default:
break;
@@ -171,89 +172,43 @@ static int upd64083_command(struct i2c_client *client, unsigned int cmd, void *a
/* i2c implementation */
-static struct i2c_driver i2c_driver;
-
-static int upd64083_attach(struct i2c_adapter *adapter, int address, int kind)
+static int upd64083_probe(struct i2c_client *client)
{
- struct i2c_client *client;
struct upd64083_state *state;
int i;
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return 0;
-
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == NULL) {
- return -ENOMEM;
- }
-
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver;
- snprintf(client->name, sizeof(client->name) - 1, "uPD64083");
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
- v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name);
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL);
- if (state == NULL) {
- kfree(client);
+ if (state == NULL)
return -ENOMEM;
- }
i2c_set_clientdata(client, state);
/* Initially assume that a ghost reduction chip is present */
state->mode = 0; /* YCS mode */
state->ext_y_adc = (1 << 5);
memcpy(state->regs, upd64083_init, TOT_REGS);
- for (i = 0; i < TOT_REGS; i++) {
+ for (i = 0; i < TOT_REGS; i++)
upd64083_write(client, i, state->regs[i]);
- }
- i2c_attach_client(client);
-
- return 0;
-}
-
-static int upd64083_probe(struct i2c_adapter *adapter)
-{
- if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, upd64083_attach);
return 0;
}
-static int upd64083_detach(struct i2c_client *client)
+static int upd64083_remove(struct i2c_client *client)
{
- int err;
-
- err = i2c_detach_client(client);
- if (err)
- return err;
-
- kfree(client);
+ kfree(i2c_get_clientdata(client));
return 0;
}
/* ----------------------------------------------------------------------- */
-/* i2c implementation */
-static struct i2c_driver i2c_driver = {
- .driver = {
- .name = "upd64083",
- },
- .id = I2C_DRIVERID_UPD64083,
- .attach_adapter = upd64083_probe,
- .detach_client = upd64083_detach,
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "upd64083",
+ .driverid = I2C_DRIVERID_UPD64083,
.command = upd64083_command,
+ .probe = upd64083_probe,
+ .remove = upd64083_remove,
};
-
-
-static int __init upd64083_init_module(void)
-{
- return i2c_add_driver(&i2c_driver);
-}
-
-static void __exit upd64083_exit_module(void)
-{
- i2c_del_driver(&i2c_driver);
-}
-
-module_init(upd64083_init_module);
-module_exit(upd64083_exit_module);
diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c
index f09eb102731b..503b13beb922 100644
--- a/drivers/media/video/usbvision/usbvision-cards.c
+++ b/drivers/media/video/usbvision/usbvision-cards.c
@@ -901,6 +901,20 @@ struct usbvision_device_data_st usbvision_device_data[] = {
.Y_Offset = -1,
.ModelString = "Pinnacle Studio PCTV USB (NTSC) FM",
},
+ [PINNA_PCTV_USB_NTSC_FM_V3] = {
+ .Interface = -1,
+ .Codec = CODEC_SAA7111,
+ .VideoChannels = 3,
+ .VideoNorm = V4L2_STD_NTSC,
+ .AudioChannels = 1,
+ .Radio = 1,
+ .vbi = 1,
+ .Tuner = 1,
+ .TunerType = TUNER_PHILIPS_NTSC_M,
+ .X_Offset = -1,
+ .Y_Offset = -1,
+ .ModelString = "Pinnacle Studio PCTV USB (NTSC) FM V3",
+ },
[PINNA_PCTV_USB_PAL_FM_V2] = {
.Interface = -1,
.Codec = CODEC_SAA7113,
@@ -1044,7 +1058,7 @@ struct usb_device_id usbvision_table [] = {
{ USB_DEVICE(0x0573, 0x4d2a), .driver_info=HPG_WINTV_PRO_NTSC_MN },
{ USB_DEVICE(0x0573, 0x4d2b), .driver_info=HPG_WINTV_PRO_NTSC_MN_V2 },
{ USB_DEVICE(0x0573, 0x4d2c), .driver_info=HPG_WINTV_PRO_PAL },
- { USB_DEVICE(0x0573, 0x4d20), .driver_info=HPG_WINTV_PRO_NTSC_MN_V3 },
+ { USB_DEVICE(0x0573, 0x4d20), .driver_info = HPG_WINTV_PRO_NTSC_MN_V3 },
{ USB_DEVICE(0x0573, 0x4d21), .driver_info=HPG_WINTV_PRO_PAL_BG },
{ USB_DEVICE(0x0573, 0x4d22), .driver_info=HPG_WINTV_PRO_PAL_I },
{ USB_DEVICE(0x0573, 0x4d23), .driver_info=HPG_WINTV_PRO_PAL_SECAM_L },
@@ -1074,6 +1088,8 @@ struct usb_device_id usbvision_table [] = {
{ USB_DEVICE(0x2304, 0x0110), .driver_info=PINNA_PCTV_USB_PAL_FM },
{ USB_DEVICE(0x2304, 0x0111), .driver_info=MIRO_PCTV_USB },
{ USB_DEVICE(0x2304, 0x0112), .driver_info=PINNA_PCTV_USB_NTSC_FM },
+ { USB_DEVICE(0x2304, 0x0113),
+ .driver_info = PINNA_PCTV_USB_NTSC_FM_V3 },
{ USB_DEVICE(0x2304, 0x0210), .driver_info=PINNA_PCTV_USB_PAL_FM_V2 },
{ USB_DEVICE(0x2304, 0x0212), .driver_info=PINNA_PCTV_USB_NTSC_FM_V2 },
{ USB_DEVICE(0x2304, 0x0214), .driver_info=PINNA_PCTV_USB_PAL_FM_V3 },
diff --git a/drivers/media/video/usbvision/usbvision-cards.h b/drivers/media/video/usbvision/usbvision-cards.h
index 512c5cee4145..9c6ad22960d8 100644
--- a/drivers/media/video/usbvision/usbvision-cards.h
+++ b/drivers/media/video/usbvision/usbvision-cards.h
@@ -62,5 +62,6 @@
#define PINNA_LINX_VD_IN_CAB_PAL 61
#define PINNA_PCTV_BUNGEE_PAL_FM 62
#define HPG_WINTV 63
+#define PINNA_PCTV_USB_NTSC_FM_V3 64
extern const int usbvision_device_data_size;
diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c
index c7d5f9ed22d7..56775ab8b75d 100644
--- a/drivers/media/video/usbvision/usbvision-core.c
+++ b/drivers/media/video/usbvision/usbvision-core.c
@@ -69,6 +69,15 @@ static int SwitchSVideoInput = 0; // To help people with Black and White outpu
module_param(SwitchSVideoInput, int, 0444);
MODULE_PARM_DESC(SwitchSVideoInput, " Set the S-Video input. Some cables and input device are wired differently. Default: 0 (Off)");
+static unsigned int adjust_X_Offset = -1;
+module_param(adjust_X_Offset, int, 0644);
+MODULE_PARM_DESC(adjust_X_Offset, "adjust X offset display [core]");
+
+static unsigned int adjust_Y_Offset = -1;
+module_param(adjust_Y_Offset, int, 0644);
+MODULE_PARM_DESC(adjust_Y_Offset, "adjust Y offset display [core]");
+
+
#define ENABLE_HEXDUMP 0 /* Enable if you need it */
@@ -624,25 +633,29 @@ static enum ParseState usbvision_parse_lines_422(struct usb_usbvision *usbvision
YUV_TO_RGB_BY_THE_BOOK(yuyv[0], yuyv[1], yuyv[3], rv, gv, bv);
switch (frame->v4l2_format.format) {
- case V4L2_PIX_FMT_RGB565:
- *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3));
- *f++ = (0x07 & (gv >> 5)) | (0xF8 & rv);
- break;
- case V4L2_PIX_FMT_RGB24:
- *f++ = bv;
- *f++ = gv;
- *f++ = rv;
- break;
- case V4L2_PIX_FMT_RGB32:
- *f++ = bv;
- *f++ = gv;
- *f++ = rv;
- f++;
- break;
- case V4L2_PIX_FMT_RGB555:
- *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2));
- *f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1));
- break;
+ case V4L2_PIX_FMT_RGB565:
+ *f++ = (0x1F & rv) |
+ (0xE0 & (gv << 5));
+ *f++ = (0x07 & (gv >> 3)) |
+ (0xF8 & bv);
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f++ = rv;
+ *f++ = gv;
+ *f++ = bv;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f++ = rv;
+ *f++ = gv;
+ *f++ = bv;
+ f++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ *f++ = (0x1F & rv) |
+ (0xE0 & (gv << 5));
+ *f++ = (0x03 & (gv >> 3)) |
+ (0x7C & (bv << 2));
+ break;
}
}
clipmask_index += clipmask_add;
@@ -656,25 +669,29 @@ static enum ParseState usbvision_parse_lines_422(struct usb_usbvision *usbvision
YUV_TO_RGB_BY_THE_BOOK(yuyv[2], yuyv[1], yuyv[3], rv, gv, bv);
switch (frame->v4l2_format.format) {
- case V4L2_PIX_FMT_RGB565:
- *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3));
- *f++ = (0x07 & (gv >> 5)) | (0xF8 & rv);
- break;
- case V4L2_PIX_FMT_RGB24:
- *f++ = bv;
- *f++ = gv;
- *f++ = rv;
- break;
- case V4L2_PIX_FMT_RGB32:
- *f++ = bv;
- *f++ = gv;
- *f++ = rv;
- f++;
- break;
- case V4L2_PIX_FMT_RGB555:
- *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2));
- *f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1));
- break;
+ case V4L2_PIX_FMT_RGB565:
+ *f++ = (0x1F & rv) |
+ (0xE0 & (gv << 5));
+ *f++ = (0x07 & (gv >> 3)) |
+ (0xF8 & bv);
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f++ = rv;
+ *f++ = gv;
+ *f++ = bv;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f++ = rv;
+ *f++ = gv;
+ *f++ = bv;
+ f++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ *f++ = (0x1F & rv) |
+ (0xE0 & (gv << 5));
+ *f++ = (0x03 & (gv >> 3)) |
+ (0x7C & (bv << 2));
+ break;
}
}
clipmask_index += clipmask_add;
@@ -942,22 +959,26 @@ static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision,
*f++ = Y[Idx];
break;
case V4L2_PIX_FMT_RGB555:
- *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2));
- *f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1));
+ *f++ = (0x1F & rv) |
+ (0xE0 & (gv << 5));
+ *f++ = (0x03 & (gv >> 3)) |
+ (0x7C & (bv << 2));
break;
case V4L2_PIX_FMT_RGB565:
- *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3));
- *f++ = (0x07 & (gv >> 5)) | (0xF8 & rv);
+ *f++ = (0x1F & rv) |
+ (0xE0 & (gv << 5));
+ *f++ = (0x07 & (gv >> 3)) |
+ (0xF8 & bv);
break;
case V4L2_PIX_FMT_RGB24:
- *f++ = bv;
- *f++ = gv;
*f++ = rv;
+ *f++ = gv;
+ *f++ = bv;
break;
case V4L2_PIX_FMT_RGB32:
- *f++ = bv;
- *f++ = gv;
*f++ = rv;
+ *f++ = gv;
+ *f++ = bv;
f++;
break;
}
@@ -1071,28 +1092,33 @@ static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision
r_ = (y_ + ur) >> 16;
switch (frame->v4l2_format.format) {
- case V4L2_PIX_FMT_RGB565:
- g = LIMIT_RGB(g_);
- *f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
- *f_even++ = (0x07 & ( g >> 5)) | (0xF8 & LIMIT_RGB(r_));
- break;
- case V4L2_PIX_FMT_RGB24:
- *f_even++ = LIMIT_RGB(b_);
- *f_even++ = LIMIT_RGB(g_);
- *f_even++ = LIMIT_RGB(r_);
- break;
- case V4L2_PIX_FMT_RGB32:
- *f_even++ = LIMIT_RGB(b_);
- *f_even++ = LIMIT_RGB(g_);
- *f_even++ = LIMIT_RGB(r_);
- f_even++;
- break;
- case V4L2_PIX_FMT_RGB555:
- g = LIMIT_RGB(g_);
- *f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
- *f_even++ = (0x03 & ( g >> 6)) |
- (0x7C & (LIMIT_RGB(r_) >> 1));
- break;
+ case V4L2_PIX_FMT_RGB565:
+ g = LIMIT_RGB(g_);
+ *f_even++ =
+ (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_even++ =
+ (0x07 & (g >> 3)) |
+ (0xF8 & LIMIT_RGB(b_));
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f_even++ = LIMIT_RGB(r_);
+ *f_even++ = LIMIT_RGB(g_);
+ *f_even++ = LIMIT_RGB(b_);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f_even++ = LIMIT_RGB(r_);
+ *f_even++ = LIMIT_RGB(g_);
+ *f_even++ = LIMIT_RGB(b_);
+ f_even++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ g = LIMIT_RGB(g_);
+ *f_even++ = (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_even++ = (0x03 & (g >> 3)) |
+ (0x7C & (LIMIT_RGB(b_) << 2));
+ break;
}
}
clipmask_even_index += clipmask_add;
@@ -1110,28 +1136,33 @@ static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision
r_ = (y_ + ur) >> 16;
switch (frame->v4l2_format.format) {
- case V4L2_PIX_FMT_RGB565:
- g = LIMIT_RGB(g_);
- *f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
- *f_even++ = (0x07 & ( g >> 5)) | (0xF8 & LIMIT_RGB(r_));
- break;
- case V4L2_PIX_FMT_RGB24:
- *f_even++ = LIMIT_RGB(b_);
- *f_even++ = LIMIT_RGB(g_);
- *f_even++ = LIMIT_RGB(r_);
- break;
- case V4L2_PIX_FMT_RGB32:
- *f_even++ = LIMIT_RGB(b_);
- *f_even++ = LIMIT_RGB(g_);
- *f_even++ = LIMIT_RGB(r_);
- f_even++;
- break;
- case V4L2_PIX_FMT_RGB555:
- g = LIMIT_RGB(g_);
- *f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
- *f_even++ = (0x03 & ( g >> 6)) |
- (0x7C & (LIMIT_RGB(r_) >> 1));
- break;
+ case V4L2_PIX_FMT_RGB565:
+ g = LIMIT_RGB(g_);
+ *f_even++ =
+ (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_even++ =
+ (0x07 & (g >> 3)) |
+ (0xF8 & LIMIT_RGB(b_));
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f_even++ = LIMIT_RGB(r_);
+ *f_even++ = LIMIT_RGB(g_);
+ *f_even++ = LIMIT_RGB(b_);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f_even++ = LIMIT_RGB(r_);
+ *f_even++ = LIMIT_RGB(g_);
+ *f_even++ = LIMIT_RGB(b_);
+ f_even++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ g = LIMIT_RGB(g_);
+ *f_even++ = (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_even++ = (0x03 & (g >> 3)) |
+ (0x7C & (LIMIT_RGB(b_) << 2));
+ break;
}
}
clipmask_even_index += clipmask_add;
@@ -1151,28 +1182,33 @@ static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision
r_ = (y_ + ur) >> 16;
switch (frame->v4l2_format.format) {
- case V4L2_PIX_FMT_RGB565:
- g = LIMIT_RGB(g_);
- *f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
- *f_odd++ = (0x07 & ( g >> 5)) | (0xF8 & LIMIT_RGB(r_));
- break;
- case V4L2_PIX_FMT_RGB24:
- *f_odd++ = LIMIT_RGB(b_);
- *f_odd++ = LIMIT_RGB(g_);
- *f_odd++ = LIMIT_RGB(r_);
- break;
- case V4L2_PIX_FMT_RGB32:
- *f_odd++ = LIMIT_RGB(b_);
- *f_odd++ = LIMIT_RGB(g_);
- *f_odd++ = LIMIT_RGB(r_);
- f_odd++;
- break;
- case V4L2_PIX_FMT_RGB555:
- g = LIMIT_RGB(g_);
- *f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
- *f_odd++ = (0x03 & ( g >> 6)) |
- (0x7C & (LIMIT_RGB(r_) >> 1));
- break;
+ case V4L2_PIX_FMT_RGB565:
+ g = LIMIT_RGB(g_);
+ *f_odd++ =
+ (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_odd++ =
+ (0x07 & (g >> 3)) |
+ (0xF8 & LIMIT_RGB(b_));
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f_odd++ = LIMIT_RGB(r_);
+ *f_odd++ = LIMIT_RGB(g_);
+ *f_odd++ = LIMIT_RGB(b_);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f_odd++ = LIMIT_RGB(r_);
+ *f_odd++ = LIMIT_RGB(g_);
+ *f_odd++ = LIMIT_RGB(b_);
+ f_odd++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ g = LIMIT_RGB(g_);
+ *f_odd++ = (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_odd++ = (0x03 & (g >> 3)) |
+ (0x7C & (LIMIT_RGB(b_) << 2));
+ break;
}
}
clipmask_odd_index += clipmask_add;
@@ -1190,28 +1226,33 @@ static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision
r_ = (y_ + ur) >> 16;
switch (frame->v4l2_format.format) {
- case V4L2_PIX_FMT_RGB565:
- g = LIMIT_RGB(g_);
- *f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
- *f_odd++ = (0x07 & ( g >> 5)) | (0xF8 & LIMIT_RGB(r_));
- break;
- case V4L2_PIX_FMT_RGB24:
- *f_odd++ = LIMIT_RGB(b_);
- *f_odd++ = LIMIT_RGB(g_);
- *f_odd++ = LIMIT_RGB(r_);
- break;
- case V4L2_PIX_FMT_RGB32:
- *f_odd++ = LIMIT_RGB(b_);
- *f_odd++ = LIMIT_RGB(g_);
- *f_odd++ = LIMIT_RGB(r_);
- f_odd++;
- break;
- case V4L2_PIX_FMT_RGB555:
- g = LIMIT_RGB(g_);
- *f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
- *f_odd++ = (0x03 & ( g >> 6)) |
- (0x7C & (LIMIT_RGB(r_) >> 1));
- break;
+ case V4L2_PIX_FMT_RGB565:
+ g = LIMIT_RGB(g_);
+ *f_odd++ =
+ (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_odd++ =
+ (0x07 & (g >> 3)) |
+ (0xF8 & LIMIT_RGB(b_));
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f_odd++ = LIMIT_RGB(r_);
+ *f_odd++ = LIMIT_RGB(g_);
+ *f_odd++ = LIMIT_RGB(b_);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f_odd++ = LIMIT_RGB(r_);
+ *f_odd++ = LIMIT_RGB(g_);
+ *f_odd++ = LIMIT_RGB(b_);
+ f_odd++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ g = LIMIT_RGB(g_);
+ *f_odd++ = (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_odd++ = (0x03 & (g >> 3)) |
+ (0x7C & (LIMIT_RGB(b_) << 2));
+ break;
}
}
clipmask_odd_index += clipmask_add;
@@ -1561,13 +1602,10 @@ static int usbvision_write_reg_irq(struct usb_usbvision *usbvision,int address,
if (len > 8) {
return -EFAULT;
}
-// down(&usbvision->ctrlUrbLock);
if (usbvision->ctrlUrbBusy) {
-// up(&usbvision->ctrlUrbLock);
return -EBUSY;
}
usbvision->ctrlUrbBusy = 1;
-// up(&usbvision->ctrlUrbLock);
usbvision->ctrlUrbSetup.bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
usbvision->ctrlUrbSetup.bRequest = USBVISION_OP_CODE;
@@ -2100,11 +2138,21 @@ int usbvision_set_input(struct usb_usbvision *usbvision)
value[5]=(usbvision_device_data[usbvision->DevModel].X_Offset & 0x0300) >> 8;
}
+ if (adjust_X_Offset != -1) {
+ value[4] = adjust_X_Offset & 0xff;
+ value[5] = (adjust_X_Offset & 0x0300) >> 8;
+ }
+
if (usbvision_device_data[usbvision->DevModel].Y_Offset >= 0) {
value[6]=usbvision_device_data[usbvision->DevModel].Y_Offset & 0xff;
value[7]=(usbvision_device_data[usbvision->DevModel].Y_Offset & 0x0300) >> 8;
}
+ if (adjust_Y_Offset != -1) {
+ value[6] = adjust_Y_Offset & 0xff;
+ value[7] = (adjust_Y_Offset & 0x0300) >> 8;
+ }
+
rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
USBVISION_OP_CODE, /* USBVISION specific code */
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0,
@@ -2242,14 +2290,18 @@ static void call_usbvision_power_off(struct work_struct *work)
struct usb_usbvision *usbvision = container_of(work, struct usb_usbvision, powerOffWork);
PDEBUG(DBG_FUNC, "");
- down_interruptible(&usbvision->lock);
+ if(mutex_lock_interruptible(&usbvision->lock)) {
+ return;
+ }
+
+
if(usbvision->user == 0) {
usbvision_i2c_unregister(usbvision);
usbvision_power_off(usbvision);
usbvision->initialized = 0;
}
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
}
static void usbvision_powerOffTimer(unsigned long data)
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index 36e689fa16c0..b52b826a30be 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -410,7 +410,7 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file)
/* If so far no errors then we shall start the camera */
if (!errCode) {
- down(&usbvision->lock);
+ mutex_lock(&usbvision->lock);
if (usbvision->power == 0) {
usbvision_power_on(usbvision);
usbvision_i2c_register(usbvision);
@@ -439,7 +439,7 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file)
usbvision->initialized = 0;
}
}
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
}
if (errCode) {
@@ -467,7 +467,7 @@ static int usbvision_v4l2_close(struct inode *inode, struct file *file)
(struct usb_usbvision *) video_get_drvdata(dev);
PDEBUG(DBG_IO, "close");
- down(&usbvision->lock);
+ mutex_lock(&usbvision->lock);
usbvision_audio_off(usbvision);
usbvision_restart_isoc(usbvision);
@@ -487,7 +487,7 @@ static int usbvision_v4l2_close(struct inode *inode, struct file *file)
usbvision->initialized = 0;
}
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
if (usbvision->remove_pending) {
printk(KERN_INFO "%s: Final disconnect\n", __FUNCTION__);
@@ -647,13 +647,13 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int input)
if ((input >= usbvision->video_inputs) || (input < 0) )
return -EINVAL;
- down(&usbvision->lock);
+ mutex_lock(&usbvision->lock);
usbvision_muxsel(usbvision, input);
usbvision_set_input(usbvision);
usbvision_set_output(usbvision,
usbvision->curwidth,
usbvision->curheight);
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
return 0;
}
@@ -664,10 +664,10 @@ static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id)
(struct usb_usbvision *) video_get_drvdata(dev);
usbvision->tvnormId=*id;
- down(&usbvision->lock);
+ mutex_lock(&usbvision->lock);
call_i2c_clients(usbvision, VIDIOC_S_STD,
&usbvision->tvnormId);
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
/* propagate the change to the decoder */
usbvision_muxsel(usbvision, usbvision->ctl_input);
@@ -1083,9 +1083,9 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv,
usbvision->curFrame = NULL;
/* by now we are committed to the new data... */
- down(&usbvision->lock);
+ mutex_lock(&usbvision->lock);
usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height);
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
return 0;
}
@@ -1211,16 +1211,16 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
PDEBUG(DBG_MMAP, "mmap");
- down(&usbvision->lock);
+ mutex_lock(&usbvision->lock);
if (!USBVISION_IS_OPERATIONAL(usbvision)) {
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
return -EFAULT;
}
if (!(vma->vm_flags & VM_WRITE) ||
size != PAGE_ALIGN(usbvision->max_frame_size)) {
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
return -EINVAL;
}
@@ -1232,7 +1232,7 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
if (i == usbvision->num_frames) {
PDEBUG(DBG_MMAP,
"mmap: user supplied mapping address is out of range");
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
return -EINVAL;
}
@@ -1245,7 +1245,7 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
PDEBUG(DBG_MMAP, "mmap: vm_insert_page failed");
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
return -EAGAIN;
}
start += PAGE_SIZE;
@@ -1253,7 +1253,7 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
size -= PAGE_SIZE;
}
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
return 0;
}
@@ -1271,7 +1271,7 @@ static int usbvision_radio_open(struct inode *inode, struct file *file)
PDEBUG(DBG_IO, "%s:", __FUNCTION__);
- down(&usbvision->lock);
+ mutex_lock(&usbvision->lock);
if (usbvision->user) {
err("%s: Someone tried to open an already opened USBVision Radio!", __FUNCTION__);
@@ -1290,7 +1290,8 @@ static int usbvision_radio_open(struct inode *inode, struct file *file)
errCode = usbvision_set_alternate(usbvision);
if (errCode < 0) {
usbvision->last_error = errCode;
- return -EBUSY;
+ errCode = -EBUSY;
+ goto out;
}
// If so far no errors then we shall start the radio
@@ -1307,7 +1308,8 @@ static int usbvision_radio_open(struct inode *inode, struct file *file)
usbvision->initialized = 0;
}
}
- up(&usbvision->lock);
+out:
+ mutex_unlock(&usbvision->lock);
return errCode;
}
@@ -1321,7 +1323,7 @@ static int usbvision_radio_close(struct inode *inode, struct file *file)
PDEBUG(DBG_IO, "");
- down(&usbvision->lock);
+ mutex_lock(&usbvision->lock);
/* Set packet size to 0 */
usbvision->ifaceAlt=0;
@@ -1337,7 +1339,7 @@ static int usbvision_radio_close(struct inode *inode, struct file *file)
usbvision->initialized = 0;
}
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
if (usbvision->remove_pending) {
printk(KERN_INFO "%s: Final disconnect\n", __FUNCTION__);
@@ -1641,7 +1643,7 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev)
usbvision->dev = dev;
- init_MUTEX(&usbvision->lock); /* to 1 == available */
+ mutex_init(&usbvision->lock); /* available */
// prepare control urb for control messages during interrupts
usbvision->ctrlUrb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
@@ -1649,7 +1651,6 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev)
goto err_exit;
}
init_waitqueue_head(&usbvision->ctrlUrb_wq);
- init_MUTEX(&usbvision->ctrlUrbLock); /* to 1 == available */
usbvision_init_powerOffTimer(usbvision);
@@ -1676,13 +1677,13 @@ static void usbvision_release(struct usb_usbvision *usbvision)
{
PDEBUG(DBG_PROBE, "");
- down(&usbvision->lock);
+ mutex_lock(&usbvision->lock);
usbvision_reset_powerOffTimer(usbvision);
usbvision->initialized = 0;
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
usbvision_remove_sysfs(usbvision->vdev);
usbvision_unregister_video(usbvision);
@@ -1796,7 +1797,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf,
}
PDEBUG(DBG_PROBE, "bridgeType %d", usbvision->bridgeType);
- down(&usbvision->lock);
+ mutex_lock(&usbvision->lock);
/* compute alternate max packet sizes */
uif = dev->actconfig->interface[0];
@@ -1807,6 +1808,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf,
usbvision->num_alt,GFP_KERNEL);
if (usbvision->alt_max_pkt_size == NULL) {
err("usbvision: out of memory!\n");
+ mutex_unlock(&usbvision->lock);
return -ENOMEM;
}
@@ -1840,7 +1842,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf,
usbvision->streaming = Stream_Off;
usbvision_register_video(usbvision);
usbvision_configure_video(usbvision);
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
usb_set_intfdata (intf, usbvision);
@@ -1871,7 +1873,7 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf)
}
usb_set_intfdata (intf, NULL);
- down(&usbvision->lock);
+ mutex_lock(&usbvision->lock);
// At this time we ask to cancel outstanding URBs
usbvision_stop_isoc(usbvision);
@@ -1885,7 +1887,7 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf)
usb_put_dev(usbvision->dev);
usbvision->dev = NULL; // USB device is no more
- up(&usbvision->lock);
+ mutex_unlock(&usbvision->lock);
if (usbvision->user) {
printk(KERN_INFO "%s: In use, disconnect pending\n",
diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h
index c5b6c501c869..20d7ec624999 100644
--- a/drivers/media/video/usbvision/usbvision.h
+++ b/drivers/media/video/usbvision/usbvision.h
@@ -34,16 +34,13 @@
#include <linux/list.h>
#include <linux/usb.h>
#include <linux/i2c.h>
+#include <linux/mutex.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
#include <linux/videodev2.h>
#define USBVISION_DEBUG /* Turn on debug messages */
-#ifndef VID_HARDWARE_USBVISION
- #define VID_HARDWARE_USBVISION 34 /* USBVision Video Grabber */
-#endif
-
#define USBVISION_PWR_REG 0x00
#define USBVISION_SSPND_EN (1 << 1)
#define USBVISION_RES2 (1 << 2)
@@ -373,7 +370,6 @@ struct usb_usbvision {
int ctrlUrbBusy;
struct usb_ctrlrequest ctrlUrbSetup;
wait_queue_head_t ctrlUrb_wq; // Processes waiting
- struct semaphore ctrlUrbLock;
/* configuration part */
int have_tuner;
@@ -396,7 +392,7 @@ struct usb_usbvision {
unsigned char iface; /* Video interface number */
unsigned char ifaceAlt; /* Alt settings */
unsigned char Vin_Reg2_Preset;
- struct semaphore lock;
+ struct mutex lock;
struct timer_list powerOffTimer;
struct work_struct powerOffWork;
int power; /* is the device powered on? */
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 1141b4bf41ce..c056ff6d810c 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -400,7 +400,7 @@ static const char *v4l2_int_ioctls[] = {
[_IOC_NR(TUNER_SET_TYPE_ADDR)] = "TUNER_SET_TYPE_ADDR",
[_IOC_NR(TUNER_SET_STANDBY)] = "TUNER_SET_STANDBY",
- [_IOC_NR(TDA9887_SET_CONFIG)] = "TDA9887_SET_CONFIG",
+ [_IOC_NR(TUNER_SET_CONFIG)] = "TUNER_SET_CONFIG",
[_IOC_NR(VIDIOC_INT_S_TUNER_MODE)] = "VIDIOC_INT_S_TUNER_MODE",
[_IOC_NR(VIDIOC_INT_RESET)] = "VIDIOC_INT_RESET",
@@ -1013,6 +1013,34 @@ int v4l2_chip_match_host(u32 match_type, u32 match_chip)
/* ----------------------------------------------------------------- */
+/* Helper function for I2C legacy drivers */
+
+int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver *driver,
+ const char *name, int (*probe)(struct i2c_client *))
+{
+ struct i2c_client *client;
+ int err;
+
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == 0)
+ return -ENOMEM;
+
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = driver;
+ strlcpy(client->name, name, sizeof(client->name));
+
+ err = probe(client);
+ if (err == 0) {
+ i2c_attach_client(client);
+ } else {
+ kfree(client);
+ }
+ return err != -ENOMEM ? 0 : err;
+}
+
+/* ----------------------------------------------------------------- */
+
EXPORT_SYMBOL(v4l2_norm_to_name);
EXPORT_SYMBOL(v4l2_video_std_construct);
@@ -1038,6 +1066,8 @@ EXPORT_SYMBOL(v4l2_chip_match_i2c_client);
EXPORT_SYMBOL(v4l2_chip_ident_i2c_client);
EXPORT_SYMBOL(v4l2_chip_match_host);
+EXPORT_SYMBOL(v4l2_i2c_attach);
+
/*
* Local variables:
* c-basic-offset: 8
diff --git a/drivers/media/video/v4l2-int-device.c b/drivers/media/video/v4l2-int-device.c
index 8b4ef530a3a8..a545dcaf857f 100644
--- a/drivers/media/video/v4l2-int-device.c
+++ b/drivers/media/video/v4l2-int-device.c
@@ -57,12 +57,12 @@ static void v4l2_int_device_try_attach_all(void)
if (!try_module_get(m->module))
continue;
- if (m->u.master->attach(m, s)) {
+ s->u.slave->master = m;
+ if (m->u.master->attach(s)) {
+ s->u.slave->master = NULL;
module_put(m->module);
continue;
}
-
- s->u.slave->master = m;
}
}
}
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c
index c8a5cb57963b..80a14da9acef 100644
--- a/drivers/media/video/videobuf-core.c
+++ b/drivers/media/video/videobuf-core.c
@@ -22,29 +22,32 @@
#include <media/videobuf-core.h>
#define MAGIC_BUFFER 0x20070728
-#define MAGIC_CHECK(is,should) if (unlikely((is) != (should))) \
- { printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); }
+#define MAGIC_CHECK(is, should) do { \
+ if (unlikely((is) != (should))) { \
+ printk(KERN_ERR "magic mismatch: %x (expected %x)\n", is, should); \
+ BUG(); } } while (0)
-static int debug = 0;
+static int debug;
module_param(debug, int, 0644);
MODULE_DESCRIPTION("helper module to manage video4linux buffers");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
MODULE_LICENSE("GPL");
-#define dprintk(level, fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "vbuf: " fmt , ## arg)
+#define dprintk(level, fmt, arg...) do { \
+ if (debug >= level) \
+ printk(KERN_DEBUG "vbuf: " fmt , ## arg); } while (0)
/* --------------------------------------------------------------------- */
#define CALL(q, f, arg...) \
- ( (q->int_ops->f)? q->int_ops->f(arg) : 0)
+ ((q->int_ops->f) ? q->int_ops->f(arg) : 0)
-void* videobuf_alloc(struct videobuf_queue* q)
+void *videobuf_alloc(struct videobuf_queue *q)
{
struct videobuf_buffer *vb;
- BUG_ON (q->msize<sizeof(*vb));
+ BUG_ON(q->msize < sizeof(*vb));
if (!q->int_ops || !q->int_ops->alloc) {
printk(KERN_ERR "No specific ops defined!\n");
@@ -66,20 +69,21 @@ int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr)
int retval = 0;
DECLARE_WAITQUEUE(wait, current);
- MAGIC_CHECK(vb->magic,MAGIC_BUFFER);
+ MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
add_wait_queue(&vb->done, &wait);
- while (vb->state == STATE_ACTIVE || vb->state == STATE_QUEUED) {
+ while (vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) {
if (non_blocking) {
retval = -EAGAIN;
break;
}
set_current_state(intr ? TASK_INTERRUPTIBLE
: TASK_UNINTERRUPTIBLE);
- if (vb->state == STATE_ACTIVE || vb->state == STATE_QUEUED)
+ if (vb->state == VIDEOBUF_ACTIVE ||
+ vb->state == VIDEOBUF_QUEUED)
schedule();
set_current_state(TASK_RUNNING);
if (intr && signal_pending(current)) {
- dprintk(1,"buffer waiton: -EINTR\n");
+ dprintk(1, "buffer waiton: -EINTR\n");
retval = -EINTR;
break;
}
@@ -88,27 +92,33 @@ int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr)
return retval;
}
-int videobuf_iolock(struct videobuf_queue* q, struct videobuf_buffer *vb,
+int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
struct v4l2_framebuffer *fbuf)
{
- MAGIC_CHECK(vb->magic,MAGIC_BUFFER);
- MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
+ MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
- /* FIXME: This is required to avoid OOPS on some cases, since mmap_mapper()
- method should be called before _iolock.
+ /* This is required to avoid OOPS on some cases,
+ since mmap_mapper() method should be called before _iolock.
On some cases, the mmap_mapper() is called only after scheduling.
-
- However, this way is just too dirty! Better to wait for some event.
*/
- schedule_timeout(HZ);
+ if (vb->memory == V4L2_MEMORY_MMAP) {
+ wait_event_timeout(vb->done, q->is_mmapped,
+ msecs_to_jiffies(100));
+ if (!q->is_mmapped) {
+ printk(KERN_ERR
+ "Error: mmap_mapper() never called!\n");
+ return -EINVAL;
+ }
+ }
- return CALL(q,iolock,q,vb,fbuf);
+ return CALL(q, iolock, q, vb, fbuf);
}
/* --------------------------------------------------------------------- */
-void videobuf_queue_core_init(struct videobuf_queue* q,
+void videobuf_queue_core_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
void *dev,
spinlock_t *irqlock,
@@ -118,7 +128,7 @@ void videobuf_queue_core_init(struct videobuf_queue* q,
void *priv,
struct videobuf_qtype_ops *int_ops)
{
- memset(q,0,sizeof(*q));
+ memset(q, 0, sizeof(*q));
q->irqlock = irqlock;
q->dev = dev;
q->type = type;
@@ -129,13 +139,13 @@ void videobuf_queue_core_init(struct videobuf_queue* q,
q->int_ops = int_ops;
/* All buffer operations are mandatory */
- BUG_ON (!q->ops->buf_setup);
- BUG_ON (!q->ops->buf_prepare);
- BUG_ON (!q->ops->buf_queue);
- BUG_ON (!q->ops->buf_release);
+ BUG_ON(!q->ops->buf_setup);
+ BUG_ON(!q->ops->buf_prepare);
+ BUG_ON(!q->ops->buf_queue);
+ BUG_ON(!q->ops->buf_release);
/* Having implementations for abstract methods are mandatory */
- BUG_ON (!q->int_ops);
+ BUG_ON(!q->int_ops);
mutex_init(&q->lock);
INIT_LIST_HEAD(&q->stream);
@@ -146,33 +156,33 @@ int videobuf_queue_is_busy(struct videobuf_queue *q)
{
int i;
- MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
if (q->streaming) {
- dprintk(1,"busy: streaming active\n");
+ dprintk(1, "busy: streaming active\n");
return 1;
}
if (q->reading) {
- dprintk(1,"busy: pending read #1\n");
+ dprintk(1, "busy: pending read #1\n");
return 1;
}
if (q->read_buf) {
- dprintk(1,"busy: pending read #2\n");
+ dprintk(1, "busy: pending read #2\n");
return 1;
}
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
if (q->bufs[i]->map) {
- dprintk(1,"busy: buffer #%d mapped\n",i);
+ dprintk(1, "busy: buffer #%d mapped\n", i);
return 1;
}
- if (q->bufs[i]->state == STATE_QUEUED) {
- dprintk(1,"busy: buffer #%d queued\n",i);
+ if (q->bufs[i]->state == VIDEOBUF_QUEUED) {
+ dprintk(1, "busy: buffer #%d queued\n", i);
return 1;
}
- if (q->bufs[i]->state == STATE_ACTIVE) {
- dprintk(1,"busy: buffer #%d avtive\n",i);
+ if (q->bufs[i]->state == VIDEOBUF_ACTIVE) {
+ dprintk(1, "busy: buffer #%d avtive\n", i);
return 1;
}
}
@@ -182,28 +192,28 @@ int videobuf_queue_is_busy(struct videobuf_queue *q)
/* Locking: Caller holds q->lock */
void videobuf_queue_cancel(struct videobuf_queue *q)
{
- unsigned long flags=0;
+ unsigned long flags = 0;
int i;
/* remove queued buffers from list */
if (q->irqlock)
- spin_lock_irqsave(q->irqlock,flags);
+ spin_lock_irqsave(q->irqlock, flags);
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
- if (q->bufs[i]->state == STATE_QUEUED) {
+ if (q->bufs[i]->state == VIDEOBUF_QUEUED) {
list_del(&q->bufs[i]->queue);
- q->bufs[i]->state = STATE_ERROR;
+ q->bufs[i]->state = VIDEOBUF_ERROR;
}
}
if (q->irqlock)
- spin_unlock_irqrestore(q->irqlock,flags);
+ spin_unlock_irqrestore(q->irqlock, flags);
/* free all buffers + clear queue */
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
- q->ops->buf_release(q,q->bufs[i]);
+ q->ops->buf_release(q, q->bufs[i]);
}
INIT_LIST_HEAD(&q->stream);
}
@@ -233,8 +243,8 @@ enum v4l2_field videobuf_next_field(struct videobuf_queue *q)
static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
struct videobuf_buffer *vb, enum v4l2_buf_type type)
{
- MAGIC_CHECK(vb->magic,MAGIC_BUFFER);
- MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
+ MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
b->index = vb->i;
b->type = type;
@@ -259,17 +269,17 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
b->flags |= V4L2_BUF_FLAG_MAPPED;
switch (vb->state) {
- case STATE_PREPARED:
- case STATE_QUEUED:
- case STATE_ACTIVE:
+ case VIDEOBUF_PREPARED:
+ case VIDEOBUF_QUEUED:
+ case VIDEOBUF_ACTIVE:
b->flags |= V4L2_BUF_FLAG_QUEUED;
break;
- case STATE_DONE:
- case STATE_ERROR:
+ case VIDEOBUF_DONE:
+ case VIDEOBUF_ERROR:
b->flags |= V4L2_BUF_FLAG_DONE;
break;
- case STATE_NEEDS_INIT:
- case STATE_IDLE:
+ case VIDEOBUF_NEEDS_INIT:
+ case VIDEOBUF_IDLE:
/* nothing */
break;
}
@@ -294,16 +304,20 @@ static int __videobuf_mmap_free(struct videobuf_queue *q)
if (!q)
return 0;
- MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+
+ rc = CALL(q, mmap_free, q);
+
+ q->is_mmapped = 0;
- rc = CALL(q,mmap_free,q);
- if (rc<0)
+ if (rc < 0)
return rc;
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
- q->ops->buf_release(q,q->bufs[i]);
+ q->ops->buf_release(q, q->bufs[i]);
kfree(q->bufs[i]);
q->bufs[i] = NULL;
}
@@ -328,7 +342,7 @@ static int __videobuf_mmap_setup(struct videobuf_queue *q,
unsigned int i;
int err;
- MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
err = __videobuf_mmap_free(q);
if (0 != err)
@@ -359,7 +373,7 @@ static int __videobuf_mmap_setup(struct videobuf_queue *q,
if (!i)
return -ENOMEM;
- dprintk(1,"mmap setup: %d buffers, %d bytes each\n",
+ dprintk(1, "mmap setup: %d buffers, %d bytes each\n",
i, bsize);
return i;
@@ -379,35 +393,35 @@ int videobuf_mmap_setup(struct videobuf_queue *q,
int videobuf_reqbufs(struct videobuf_queue *q,
struct v4l2_requestbuffers *req)
{
- unsigned int size,count;
+ unsigned int size, count;
int retval;
if (req->count < 1) {
- dprintk(1,"reqbufs: count invalid (%d)\n",req->count);
+ dprintk(1, "reqbufs: count invalid (%d)\n", req->count);
return -EINVAL;
}
if (req->memory != V4L2_MEMORY_MMAP &&
req->memory != V4L2_MEMORY_USERPTR &&
req->memory != V4L2_MEMORY_OVERLAY) {
- dprintk(1,"reqbufs: memory type invalid\n");
+ dprintk(1, "reqbufs: memory type invalid\n");
return -EINVAL;
}
mutex_lock(&q->lock);
if (req->type != q->type) {
- dprintk(1,"reqbufs: queue type invalid\n");
+ dprintk(1, "reqbufs: queue type invalid\n");
retval = -EINVAL;
goto done;
}
if (q->streaming) {
- dprintk(1,"reqbufs: streaming already exists\n");
+ dprintk(1, "reqbufs: streaming already exists\n");
retval = -EBUSY;
goto done;
}
if (!list_empty(&q->stream)) {
- dprintk(1,"reqbufs: stream running\n");
+ dprintk(1, "reqbufs: stream running\n");
retval = -EBUSY;
goto done;
}
@@ -416,14 +430,14 @@ int videobuf_reqbufs(struct videobuf_queue *q,
if (count > VIDEO_MAX_FRAME)
count = VIDEO_MAX_FRAME;
size = 0;
- q->ops->buf_setup(q,&count,&size);
+ q->ops->buf_setup(q, &count, &size);
size = PAGE_ALIGN(size);
- dprintk(1,"reqbufs: bufs=%d, size=0x%x [%d pages total]\n",
+ dprintk(1, "reqbufs: bufs=%d, size=0x%x [%d pages total]\n",
count, size, (count*size)>>PAGE_SHIFT);
- retval = __videobuf_mmap_setup(q,count,size,req->memory);
+ retval = __videobuf_mmap_setup(q, count, size, req->memory);
if (retval < 0) {
- dprintk(1,"reqbufs: mmap setup returned %d\n",retval);
+ dprintk(1, "reqbufs: mmap setup returned %d\n", retval);
goto done;
}
@@ -440,19 +454,19 @@ int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
mutex_lock(&q->lock);
if (unlikely(b->type != q->type)) {
- dprintk(1,"querybuf: Wrong type.\n");
+ dprintk(1, "querybuf: Wrong type.\n");
goto done;
}
if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME)) {
- dprintk(1,"querybuf: index out of range.\n");
+ dprintk(1, "querybuf: index out of range.\n");
goto done;
}
if (unlikely(NULL == q->bufs[b->index])) {
- dprintk(1,"querybuf: buffer is null.\n");
+ dprintk(1, "querybuf: buffer is null.\n");
goto done;
}
- videobuf_status(q,b,q->bufs[b->index],q->type);
+ videobuf_status(q, b, q->bufs[b->index], q->type);
ret = 0;
done:
@@ -465,10 +479,10 @@ int videobuf_qbuf(struct videobuf_queue *q,
{
struct videobuf_buffer *buf;
enum v4l2_field field;
- unsigned long flags=0;
+ unsigned long flags = 0;
int retval;
- MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
if (b->memory == V4L2_MEMORY_MMAP)
down_read(&current->mm->mmap_sem);
@@ -476,36 +490,36 @@ int videobuf_qbuf(struct videobuf_queue *q,
mutex_lock(&q->lock);
retval = -EBUSY;
if (q->reading) {
- dprintk(1,"qbuf: Reading running...\n");
+ dprintk(1, "qbuf: Reading running...\n");
goto done;
}
retval = -EINVAL;
if (b->type != q->type) {
- dprintk(1,"qbuf: Wrong type.\n");
+ dprintk(1, "qbuf: Wrong type.\n");
goto done;
}
if (b->index < 0 || b->index >= VIDEO_MAX_FRAME) {
- dprintk(1,"qbuf: index out of range.\n");
+ dprintk(1, "qbuf: index out of range.\n");
goto done;
}
buf = q->bufs[b->index];
if (NULL == buf) {
- dprintk(1,"qbuf: buffer is null.\n");
+ dprintk(1, "qbuf: buffer is null.\n");
goto done;
}
- MAGIC_CHECK(buf->magic,MAGIC_BUFFER);
+ MAGIC_CHECK(buf->magic, MAGIC_BUFFER);
if (buf->memory != b->memory) {
- dprintk(1,"qbuf: memory type is wrong.\n");
+ dprintk(1, "qbuf: memory type is wrong.\n");
goto done;
}
- if (buf->state != STATE_NEEDS_INIT && buf->state != STATE_IDLE) {
- dprintk(1,"qbuf: buffer is already queued or active.\n");
+ if (buf->state != VIDEOBUF_NEEDS_INIT && buf->state != VIDEOBUF_IDLE) {
+ dprintk(1, "qbuf: buffer is already queued or active.\n");
goto done;
}
if (b->flags & V4L2_BUF_FLAG_INPUT) {
if (b->input >= q->inputs) {
- dprintk(1,"qbuf: wrong input.\n");
+ dprintk(1, "qbuf: wrong input.\n");
goto done;
}
buf->input = b->input;
@@ -516,44 +530,46 @@ int videobuf_qbuf(struct videobuf_queue *q,
switch (b->memory) {
case V4L2_MEMORY_MMAP:
if (0 == buf->baddr) {
- dprintk(1,"qbuf: mmap requested but buffer addr is zero!\n");
+ dprintk(1, "qbuf: mmap requested "
+ "but buffer addr is zero!\n");
goto done;
}
break;
case V4L2_MEMORY_USERPTR:
if (b->length < buf->bsize) {
- dprintk(1,"qbuf: buffer length is not enough\n");
+ dprintk(1, "qbuf: buffer length is not enough\n");
goto done;
}
- if (STATE_NEEDS_INIT != buf->state && buf->baddr != b->m.userptr)
- q->ops->buf_release(q,buf);
+ if (VIDEOBUF_NEEDS_INIT != buf->state &&
+ buf->baddr != b->m.userptr)
+ q->ops->buf_release(q, buf);
buf->baddr = b->m.userptr;
break;
case V4L2_MEMORY_OVERLAY:
buf->boff = b->m.offset;
break;
default:
- dprintk(1,"qbuf: wrong memory type\n");
+ dprintk(1, "qbuf: wrong memory type\n");
goto done;
}
- dprintk(1,"qbuf: requesting next field\n");
+ dprintk(1, "qbuf: requesting next field\n");
field = videobuf_next_field(q);
- retval = q->ops->buf_prepare(q,buf,field);
+ retval = q->ops->buf_prepare(q, buf, field);
if (0 != retval) {
- dprintk(1,"qbuf: buffer_prepare returned %d\n",retval);
+ dprintk(1, "qbuf: buffer_prepare returned %d\n", retval);
goto done;
}
- list_add_tail(&buf->stream,&q->stream);
+ list_add_tail(&buf->stream, &q->stream);
if (q->streaming) {
if (q->irqlock)
- spin_lock_irqsave(q->irqlock,flags);
- q->ops->buf_queue(q,buf);
+ spin_lock_irqsave(q->irqlock, flags);
+ q->ops->buf_queue(q, buf);
if (q->irqlock)
- spin_unlock_irqrestore(q->irqlock,flags);
+ spin_unlock_irqrestore(q->irqlock, flags);
}
- dprintk(1,"qbuf: succeded\n");
+ dprintk(1, "qbuf: succeded\n");
retval = 0;
done:
@@ -571,49 +587,49 @@ int videobuf_dqbuf(struct videobuf_queue *q,
struct videobuf_buffer *buf;
int retval;
- MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
mutex_lock(&q->lock);
retval = -EBUSY;
if (q->reading) {
- dprintk(1,"dqbuf: Reading running...\n");
+ dprintk(1, "dqbuf: Reading running...\n");
goto done;
}
retval = -EINVAL;
if (b->type != q->type) {
- dprintk(1,"dqbuf: Wrong type.\n");
+ dprintk(1, "dqbuf: Wrong type.\n");
goto done;
}
if (list_empty(&q->stream)) {
- dprintk(1,"dqbuf: stream running\n");
+ dprintk(1, "dqbuf: stream running\n");
goto done;
}
buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
retval = videobuf_waiton(buf, nonblocking, 1);
if (retval < 0) {
- dprintk(1,"dqbuf: waiton returned %d\n",retval);
+ dprintk(1, "dqbuf: waiton returned %d\n", retval);
goto done;
}
switch (buf->state) {
- case STATE_ERROR:
- dprintk(1,"dqbuf: state is error\n");
+ case VIDEOBUF_ERROR:
+ dprintk(1, "dqbuf: state is error\n");
retval = -EIO;
- CALL(q,sync,q, buf);
- buf->state = STATE_IDLE;
+ CALL(q, sync, q, buf);
+ buf->state = VIDEOBUF_IDLE;
break;
- case STATE_DONE:
- dprintk(1,"dqbuf: state is done\n");
- CALL(q,sync,q, buf);
- buf->state = STATE_IDLE;
+ case VIDEOBUF_DONE:
+ dprintk(1, "dqbuf: state is done\n");
+ CALL(q, sync, q, buf);
+ buf->state = VIDEOBUF_IDLE;
break;
default:
- dprintk(1,"dqbuf: state invalid\n");
+ dprintk(1, "dqbuf: state invalid\n");
retval = -EINVAL;
goto done;
}
list_del(&buf->stream);
- memset(b,0,sizeof(*b));
- videobuf_status(q,b,buf,q->type);
+ memset(b, 0, sizeof(*b));
+ videobuf_status(q, b, buf, q->type);
done:
mutex_unlock(&q->lock);
@@ -623,7 +639,7 @@ int videobuf_dqbuf(struct videobuf_queue *q,
int videobuf_streamon(struct videobuf_queue *q)
{
struct videobuf_buffer *buf;
- unsigned long flags=0;
+ unsigned long flags = 0;
int retval;
mutex_lock(&q->lock);
@@ -635,12 +651,12 @@ int videobuf_streamon(struct videobuf_queue *q)
goto done;
q->streaming = 1;
if (q->irqlock)
- spin_lock_irqsave(q->irqlock,flags);
+ spin_lock_irqsave(q->irqlock, flags);
list_for_each_entry(buf, &q->stream, stream)
- if (buf->state == STATE_PREPARED)
- q->ops->buf_queue(q,buf);
+ if (buf->state == VIDEOBUF_PREPARED)
+ q->ops->buf_queue(q, buf);
if (q->irqlock)
- spin_unlock_irqrestore(q->irqlock,flags);
+ spin_unlock_irqrestore(q->irqlock, flags);
done:
mutex_unlock(&q->lock);
@@ -676,10 +692,10 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q,
size_t count, loff_t *ppos)
{
enum v4l2_field field;
- unsigned long flags=0;
+ unsigned long flags = 0;
int retval;
- MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
/* setup stuff */
q->read_buf = videobuf_alloc(q);
@@ -691,20 +707,20 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q,
q->read_buf->bsize = count;
field = videobuf_next_field(q);
- retval = q->ops->buf_prepare(q,q->read_buf,field);
+ retval = q->ops->buf_prepare(q, q->read_buf, field);
if (0 != retval)
goto done;
/* start capture & wait */
if (q->irqlock)
- spin_lock_irqsave(q->irqlock,flags);
- q->ops->buf_queue(q,q->read_buf);
+ spin_lock_irqsave(q->irqlock, flags);
+ q->ops->buf_queue(q, q->read_buf);
if (q->irqlock)
- spin_unlock_irqrestore(q->irqlock,flags);
- retval = videobuf_waiton(q->read_buf,0,0);
+ spin_unlock_irqrestore(q->irqlock, flags);
+ retval = videobuf_waiton(q->read_buf, 0, 0);
if (0 == retval) {
- CALL(q,sync,q,q->read_buf);
- if (STATE_ERROR == q->read_buf->state)
+ CALL(q, sync, q, q->read_buf);
+ if (VIDEOBUF_ERROR == q->read_buf->state)
retval = -EIO;
else
retval = q->read_buf->size;
@@ -712,7 +728,7 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q,
done:
/* cleanup */
- q->ops->buf_release(q,q->read_buf);
+ q->ops->buf_release(q, q->read_buf);
kfree(q->read_buf);
q->read_buf = NULL;
return retval;
@@ -723,21 +739,21 @@ ssize_t videobuf_read_one(struct videobuf_queue *q,
int nonblocking)
{
enum v4l2_field field;
- unsigned long flags=0;
+ unsigned long flags = 0;
unsigned size, nbufs;
int retval;
- MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
mutex_lock(&q->lock);
nbufs = 1; size = 0;
- q->ops->buf_setup(q,&nbufs,&size);
+ q->ops->buf_setup(q, &nbufs, &size);
if (NULL == q->read_buf &&
count >= size &&
!nonblocking) {
- retval = videobuf_read_zerocopy(q,data,count,ppos);
+ retval = videobuf_read_zerocopy(q, data, count, ppos);
if (retval >= 0 || retval == -EIO)
/* ok, all done */
goto done;
@@ -749,25 +765,25 @@ ssize_t videobuf_read_one(struct videobuf_queue *q,
retval = -ENOMEM;
q->read_buf = videobuf_alloc(q);
- dprintk(1,"video alloc=0x%p\n", q->read_buf);
+ dprintk(1, "video alloc=0x%p\n", q->read_buf);
if (NULL == q->read_buf)
goto done;
q->read_buf->memory = V4L2_MEMORY_USERPTR;
q->read_buf->bsize = count; /* preferred size */
field = videobuf_next_field(q);
- retval = q->ops->buf_prepare(q,q->read_buf,field);
+ retval = q->ops->buf_prepare(q, q->read_buf, field);
if (0 != retval) {
- kfree (q->read_buf);
+ kfree(q->read_buf);
q->read_buf = NULL;
goto done;
}
if (q->irqlock)
- spin_lock_irqsave(q->irqlock,flags);
+ spin_lock_irqsave(q->irqlock, flags);
- q->ops->buf_queue(q,q->read_buf);
+ q->ops->buf_queue(q, q->read_buf);
if (q->irqlock)
- spin_unlock_irqrestore(q->irqlock,flags);
+ spin_unlock_irqrestore(q->irqlock, flags);
q->read_off = 0;
}
@@ -776,11 +792,11 @@ ssize_t videobuf_read_one(struct videobuf_queue *q,
if (0 != retval)
goto done;
- CALL(q,sync,q,q->read_buf);
+ CALL(q, sync, q, q->read_buf);
- if (STATE_ERROR == q->read_buf->state) {
+ if (VIDEOBUF_ERROR == q->read_buf->state) {
/* catch I/O errors */
- q->ops->buf_release(q,q->read_buf);
+ q->ops->buf_release(q, q->read_buf);
kfree(q->read_buf);
q->read_buf = NULL;
retval = -EIO;
@@ -788,14 +804,14 @@ ssize_t videobuf_read_one(struct videobuf_queue *q,
}
/* Copy to userspace */
- retval=CALL(q,video_copy_to_user,q,data,count,nonblocking);
- if (retval<0)
+ retval = CALL(q, video_copy_to_user, q, data, count, nonblocking);
+ if (retval < 0)
goto done;
q->read_off += retval;
if (q->read_off == q->read_buf->size) {
/* all data copied, cleanup */
- q->ops->buf_release(q,q->read_buf);
+ q->ops->buf_release(q, q->read_buf);
kfree(q->read_buf);
q->read_buf = NULL;
}
@@ -806,14 +822,14 @@ ssize_t videobuf_read_one(struct videobuf_queue *q,
}
/* Locking: Caller holds q->lock */
-int __videobuf_read_start(struct videobuf_queue *q)
+static int __videobuf_read_start(struct videobuf_queue *q)
{
enum v4l2_field field;
- unsigned long flags=0;
+ unsigned long flags = 0;
unsigned int count = 0, size = 0;
int err, i;
- q->ops->buf_setup(q,&count,&size);
+ q->ops->buf_setup(q, &count, &size);
if (count < 2)
count = 2;
if (count > VIDEO_MAX_FRAME)
@@ -828,17 +844,17 @@ int __videobuf_read_start(struct videobuf_queue *q)
for (i = 0; i < count; i++) {
field = videobuf_next_field(q);
- err = q->ops->buf_prepare(q,q->bufs[i],field);
+ err = q->ops->buf_prepare(q, q->bufs[i], field);
if (err)
return err;
list_add_tail(&q->bufs[i]->stream, &q->stream);
}
if (q->irqlock)
- spin_lock_irqsave(q->irqlock,flags);
+ spin_lock_irqsave(q->irqlock, flags);
for (i = 0; i < count; i++)
- q->ops->buf_queue(q,q->bufs[i]);
+ q->ops->buf_queue(q, q->bufs[i]);
if (q->irqlock)
- spin_unlock_irqrestore(q->irqlock,flags);
+ spin_unlock_irqrestore(q->irqlock, flags);
q->reading = 1;
return 0;
}
@@ -859,7 +875,7 @@ static void __videobuf_read_stop(struct videobuf_queue *q)
}
q->read_buf = NULL;
q->reading = 0;
-
+
}
int videobuf_read_start(struct videobuf_queue *q)
@@ -899,11 +915,11 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q,
int vbihack, int nonblocking)
{
int rc, retval;
- unsigned long flags=0;
+ unsigned long flags = 0;
- MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
- dprintk(2,"%s\n",__FUNCTION__);
+ dprintk(2, "%s\n", __FUNCTION__);
mutex_lock(&q->lock);
retval = -EBUSY;
if (q->streaming)
@@ -931,8 +947,8 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q,
break;
}
- if (q->read_buf->state == STATE_DONE) {
- rc = CALL (q,copy_stream, q, data + retval, count,
+ if (q->read_buf->state == VIDEOBUF_DONE) {
+ rc = CALL(q, copy_stream, q, data + retval, count,
retval, vbihack, nonblocking);
if (rc < 0) {
retval = rc;
@@ -953,10 +969,10 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q,
list_add_tail(&q->read_buf->stream,
&q->stream);
if (q->irqlock)
- spin_lock_irqsave(q->irqlock,flags);
- q->ops->buf_queue(q,q->read_buf);
+ spin_lock_irqsave(q->irqlock, flags);
+ q->ops->buf_queue(q, q->read_buf);
if (q->irqlock)
- spin_unlock_irqrestore(q->irqlock,flags);
+ spin_unlock_irqrestore(q->irqlock, flags);
q->read_buf = NULL;
}
if (retval < 0)
@@ -999,8 +1015,8 @@ unsigned int videobuf_poll_stream(struct file *file,
if (0 == rc) {
poll_wait(file, &buf->done, wait);
- if (buf->state == STATE_DONE ||
- buf->state == STATE_ERROR)
+ if (buf->state == VIDEOBUF_DONE ||
+ buf->state == VIDEOBUF_ERROR)
rc = POLLIN|POLLRDNORM;
}
mutex_unlock(&q->lock);
@@ -1012,10 +1028,11 @@ int videobuf_mmap_mapper(struct videobuf_queue *q,
{
int retval;
- MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
mutex_lock(&q->lock);
- retval=CALL(q,mmap_mapper,q,vma);
+ retval = CALL(q, mmap_mapper, q, vma);
+ q->is_mmapped = 1;
mutex_unlock(&q->lock);
return retval;
@@ -1026,15 +1043,15 @@ int videobuf_cgmbuf(struct videobuf_queue *q,
struct video_mbuf *mbuf, int count)
{
struct v4l2_requestbuffers req;
- int rc,i;
+ int rc, i;
- MAGIC_CHECK(q->int_ops->magic,MAGIC_QTYPE_OPS);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
- memset(&req,0,sizeof(req));
+ memset(&req, 0, sizeof(req));
req.type = q->type;
req.count = count;
req.memory = V4L2_MEMORY_MMAP;
- rc = videobuf_reqbufs(q,&req);
+ rc = videobuf_reqbufs(q, &req);
if (rc < 0)
return rc;
@@ -1079,9 +1096,3 @@ EXPORT_SYMBOL_GPL(videobuf_poll_stream);
EXPORT_SYMBOL_GPL(videobuf_mmap_setup);
EXPORT_SYMBOL_GPL(videobuf_mmap_free);
EXPORT_SYMBOL_GPL(videobuf_mmap_mapper);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c
index 44ee408e145f..98efd7ab1f50 100644
--- a/drivers/media/video/videobuf-dma-sg.c
+++ b/drivers/media/video/videobuf-dma-sg.c
@@ -385,30 +385,27 @@ videobuf_vm_close(struct vm_area_struct *vma)
* now ...). Bounce buffers don't work very well for the data rates
* video capture has.
*/
-static struct page*
-videobuf_vm_nopage(struct vm_area_struct *vma, unsigned long vaddr,
- int *type)
+static int
+videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct page *page;
- dprintk(3,"nopage: fault @ %08lx [vma %08lx-%08lx]\n",
- vaddr,vma->vm_start,vma->vm_end);
- if (vaddr > vma->vm_end)
- return NOPAGE_SIGBUS;
+ dprintk(3,"fault: fault @ %08lx [vma %08lx-%08lx]\n",
+ (unsigned long)vmf->virtual_address,vma->vm_start,vma->vm_end);
page = alloc_page(GFP_USER | __GFP_DMA32);
if (!page)
- return NOPAGE_OOM;
- clear_user_page(page_address(page), vaddr, page);
- if (type)
- *type = VM_FAULT_MINOR;
- return page;
+ return VM_FAULT_OOM;
+ clear_user_page(page_address(page), (unsigned long)vmf->virtual_address,
+ page);
+ vmf->page = page;
+ return 0;
}
static struct vm_operations_struct videobuf_vm_ops =
{
.open = videobuf_vm_open,
.close = videobuf_vm_close,
- .nopage = videobuf_vm_nopage,
+ .fault = videobuf_vm_fault,
};
/* ---------------------------------------------------------------------
diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c
index 880317e04a02..b73aba65d21d 100644
--- a/drivers/media/video/videobuf-dvb.c
+++ b/drivers/media/video/videobuf-dvb.c
@@ -67,7 +67,7 @@ static int videobuf_dvb_thread(void *data)
/* feed buffer data to demux */
dma=videobuf_to_dma(buf);
- if (buf->state == STATE_DONE)
+ if (buf->state == VIDEOBUF_DONE)
dvb_dmx_swfilter(&dvb->demux, dma->vmalloc,
buf->size);
diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c
index e01259438bb2..9b3898347ca5 100644
--- a/drivers/media/video/videobuf-vmalloc.c
+++ b/drivers/media/video/videobuf-vmalloc.c
@@ -41,7 +41,7 @@ MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
MODULE_LICENSE("GPL");
#define dprintk(level, fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "vbuf-sg: " fmt , ## arg)
+ printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg)
/***************************************************************************/
diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c
index 9611c3990285..28655f8983c6 100644
--- a/drivers/media/video/videodev.c
+++ b/drivers/media/video/videodev.c
@@ -973,7 +973,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
*id = vfd->current_norm;
- dbgarg (cmd, "value=%Lu\n", (long long unsigned) *id);
+ dbgarg (cmd, "value=%08Lx\n", (long long unsigned) *id);
ret=0;
break;
@@ -982,7 +982,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
{
v4l2_std_id *id = arg,norm;
- dbgarg (cmd, "value=%Lu\n", (long long unsigned) *id);
+ dbgarg (cmd, "value=%08Lx\n", (long long unsigned) *id);
norm = (*id) & vfd->tvnorms;
if ( vfd->tvnorms && !norm) /* Check if std is supported */
@@ -1008,7 +1008,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
break;
ret=vfd->vidioc_querystd(file, fh, arg);
if (!ret)
- dbgarg (cmd, "detected std=%Lu\n",
+ dbgarg (cmd, "detected std=%08Lx\n",
(unsigned long long)*p);
break;
}
@@ -1028,7 +1028,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
if (!ret)
dbgarg (cmd, "index=%d, name=%s, type=%d, "
"audioset=%d, "
- "tuner=%d, std=%Ld, status=%d\n",
+ "tuner=%d, std=%08Lx, status=%d\n",
p->index,p->name,p->type,p->audioset,
p->tuner,
(unsigned long long)p->std,
diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
index 9a03dc82c6ca..5bb75294b5aa 100644
--- a/drivers/media/video/vino.c
+++ b/drivers/media/video/vino.c
@@ -2589,11 +2589,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs)
/* First try D1 and then SAA7191 */
if (vino_drvdata->camera.driver
&& (vino_drvdata->camera.owner == VINO_NO_CHANNEL)) {
- if (i2c_use_client(vino_drvdata->camera.driver)) {
- ret = -ENODEV;
- goto out;
- }
-
+ i2c_use_client(vino_drvdata->camera.driver);
vino_drvdata->camera.owner = vcs->channel;
vcs->input = VINO_INPUT_D1;
vcs->data_norm = VINO_DATA_NORM_D1;
@@ -2602,11 +2598,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs)
int input, data_norm;
int saa7191_input;
- if (i2c_use_client(vino_drvdata->decoder.driver)) {
- ret = -ENODEV;
- goto out;
- }
-
+ i2c_use_client(vino_drvdata->decoder.driver);
input = VINO_INPUT_COMPOSITE;
saa7191_input = vino_get_saa7191_input(input);
@@ -2688,10 +2680,7 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input)
}
if (vino_drvdata->decoder.owner == VINO_NO_CHANNEL) {
- if (i2c_use_client(vino_drvdata->decoder.driver)) {
- ret = -ENODEV;
- goto out;
- }
+ i2c_use_client(vino_drvdata->decoder.driver);
vino_drvdata->decoder.owner = vcs->channel;
}
@@ -2759,10 +2748,7 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input)
}
if (vino_drvdata->camera.owner == VINO_NO_CHANNEL) {
- if (i2c_use_client(vino_drvdata->camera.driver)) {
- ret = -ENODEV;
- goto out;
- }
+ i2c_use_client(vino_drvdata->camera.driver);
vino_drvdata->camera.owner = vcs->channel;
}
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 9b54ff9d2e36..1db067c02815 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -44,21 +44,18 @@
#define WAKE_DENOMINATOR 1001
#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
-/* These timers are for 1 fps - used only for testing */
-//#define WAKE_DENOMINATOR 30 /* hack for testing purposes */
-//#define BUFFER_TIMEOUT msecs_to_jiffies(5000) /* 5 seconds */
-
#include "font.h"
#define VIVI_MAJOR_VERSION 0
#define VIVI_MINOR_VERSION 4
#define VIVI_RELEASE 0
-#define VIVI_VERSION KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE)
+#define VIVI_VERSION \
+ KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE)
/* Declare static vars that will be used as parameters */
static unsigned int vid_limit = 16; /* Video memory limit, in Mb */
-static struct video_device vivi; /* Video device */
static int video_nr = -1; /* /dev/videoN, -1 for autodetect */
+static int n_devs = 1; /* Number of virtual devices */
/* supported controls */
static struct v4l2_queryctrl vivi_qctrl[] = {
@@ -71,7 +68,7 @@ static struct v4l2_queryctrl vivi_qctrl[] = {
.default_value = 65535,
.flags = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
- },{
+ }, {
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
@@ -112,9 +109,9 @@ static struct v4l2_queryctrl vivi_qctrl[] = {
static int qctl_regs[ARRAY_SIZE(vivi_qctrl)];
-#define dprintk(level,fmt, arg...) \
+#define dprintk(dev, level, fmt, arg...) \
do { \
- if (vivi.debug >= (level)) \
+ if (dev->vfd->debug >= (level)) \
printk(KERN_DEBUG "vivi: " fmt , ## arg); \
} while (0)
@@ -166,17 +163,21 @@ struct vivi_dev {
struct list_head vivi_devlist;
struct mutex lock;
+ spinlock_t slock;
int users;
/* various device info */
- struct video_device vfd;
+ struct video_device *vfd;
struct vivi_dmaqueue vidq;
/* Several counters */
- int h,m,s,us,jiffies;
+ int h, m, s, ms;
+ unsigned long jiffies;
char timestr[13];
+
+ int mv_count; /* Controls bars movement */
};
struct vivi_fh {
@@ -184,7 +185,7 @@ struct vivi_fh {
/* video capture */
struct vivi_fmt *fmt;
- unsigned int width,height;
+ unsigned int width, height;
struct videobuf_queue vb_vidq;
enum v4l2_buf_type type;
@@ -203,109 +204,113 @@ enum colors {
GREEN,
MAGENTA,
RED,
- BLUE
+ BLUE,
+ BLACK,
};
static u8 bars[8][3] = {
/* R G B */
- {204,204,204}, /* white */
- {208,208, 0}, /* ambar */
- { 0,206,206}, /* cyan */
- { 0,239, 0}, /* green */
- {239, 0,239}, /* magenta */
- {205, 0, 0}, /* red */
- { 0, 0,255}, /* blue */
- { 0, 0, 0}
+ {204, 204, 204}, /* white */
+ {208, 208, 0}, /* ambar */
+ { 0, 206, 206}, /* cyan */
+ { 0, 239, 0}, /* green */
+ {239, 0, 239}, /* magenta */
+ {205, 0, 0}, /* red */
+ { 0, 0, 255}, /* blue */
+ { 0, 0, 0}, /* black */
};
-#define TO_Y(r,g,b) (((16829*r +33039*g +6416*b + 32768)>>16)+16)
+#define TO_Y(r, g, b) \
+ (((16829 * r + 33039 * g + 6416 * b + 32768) >> 16) + 16)
/* RGB to V(Cr) Color transform */
-#define TO_V(r,g,b) (((28784*r -24103*g -4681*b + 32768)>>16)+128)
+#define TO_V(r, g, b) \
+ (((28784 * r - 24103 * g - 4681 * b + 32768) >> 16) + 128)
/* RGB to U(Cb) Color transform */
-#define TO_U(r,g,b) (((-9714*r -19070*g +28784*b + 32768)>>16)+128)
+#define TO_U(r, g, b) \
+ (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128)
#define TSTAMP_MIN_Y 24
#define TSTAMP_MAX_Y TSTAMP_MIN_Y+15
#define TSTAMP_MIN_X 64
-static void gen_line(char *basep,int inipos,int wmax,
- int hmax, int line, int count, char *timestr)
+static void gen_line(char *basep, int inipos, int wmax,
+ int hmax, int line, int count, char *timestr)
{
- int w,i,j,pos=inipos,y;
- char *p,*s;
- u8 chr,r,g,b,color;
+ int w, i, j, y;
+ int pos = inipos;
+ char *p, *s;
+ u8 chr, r, g, b, color;
/* We will just duplicate the second pixel at the packet */
- wmax/=2;
+ wmax /= 2;
/* Generate a standard color bar pattern */
- for (w=0;w<wmax;w++) {
- int colorpos=((w+count)*8/(wmax+1)) % 8;
- r=bars[colorpos][0];
- g=bars[colorpos][1];
- b=bars[colorpos][2];
+ for (w = 0; w < wmax; w++) {
+ int colorpos = ((w + count) * 8/(wmax + 1)) % 8;
+ r = bars[colorpos][0];
+ g = bars[colorpos][1];
+ b = bars[colorpos][2];
- for (color=0;color<4;color++) {
- p=basep+pos;
+ for (color = 0; color < 4; color++) {
+ p = basep + pos;
switch (color) {
- case 0:
- case 2:
- *p=TO_Y(r,g,b); /* Luminance */
- break;
- case 1:
- *p=TO_U(r,g,b); /* Cb */
- break;
- case 3:
- *p=TO_V(r,g,b); /* Cr */
- break;
+ case 0:
+ case 2:
+ *p = TO_Y(r, g, b); /* Luma */
+ break;
+ case 1:
+ *p = TO_U(r, g, b); /* Cb */
+ break;
+ case 3:
+ *p = TO_V(r, g, b); /* Cr */
+ break;
}
pos++;
}
}
/* Checks if it is possible to show timestamp */
- if (TSTAMP_MAX_Y>=hmax)
+ if (TSTAMP_MAX_Y >= hmax)
goto end;
- if (TSTAMP_MIN_X+strlen(timestr)>=wmax)
+ if (TSTAMP_MIN_X + strlen(timestr) >= wmax)
goto end;
/* Print stream time */
- if (line>=TSTAMP_MIN_Y && line<=TSTAMP_MAX_Y) {
- j=TSTAMP_MIN_X;
- for (s=timestr;*s;s++) {
- chr=rom8x16_bits[(*s-0x30)*16+line-TSTAMP_MIN_Y];
- for (i=0;i<7;i++) {
- if (chr&1<<(7-i)) { /* Font color*/
- r=bars[BLUE][0];
- g=bars[BLUE][1];
- b=bars[BLUE][2];
- r=g=b=0;
- g=198;
- } else { /* Background color */
- r=bars[WHITE][0];
- g=bars[WHITE][1];
- b=bars[WHITE][2];
- r=g=b=0;
+ if (line >= TSTAMP_MIN_Y && line <= TSTAMP_MAX_Y) {
+ j = TSTAMP_MIN_X;
+ for (s = timestr; *s; s++) {
+ chr = rom8x16_bits[(*s-0x30)*16+line-TSTAMP_MIN_Y];
+ for (i = 0; i < 7; i++) {
+ if (chr & 1 << (7 - i)) {
+ /* Font color*/
+ r = 0;
+ g = 198;
+ b = 0;
+ } else {
+ /* Background color */
+ r = bars[BLACK][0];
+ g = bars[BLACK][1];
+ b = bars[BLACK][2];
}
- pos=inipos+j*2;
- for (color=0;color<4;color++) {
- p=basep+pos;
+ pos = inipos + j * 2;
+ for (color = 0; color < 4; color++) {
+ p = basep + pos;
- y=TO_Y(r,g,b);
+ y = TO_Y(r, g, b);
switch (color) {
- case 0:
- case 2:
- *p=TO_Y(r,g,b); /* Luminance */
- break;
- case 1:
- *p=TO_U(r,g,b); /* Cb */
- break;
- case 3:
- *p=TO_V(r,g,b); /* Cr */
- break;
+ case 0:
+ case 2:
+ *p = TO_Y(r, g, b); /* Luma */
+ break;
+ case 1:
+ *p = TO_U(r, g, b); /* Cb */
+ break;
+ case 3:
+ *p = TO_V(r, g, b); /* Cr */
+ break;
}
pos++;
}
@@ -314,63 +319,60 @@ static void gen_line(char *basep,int inipos,int wmax,
}
}
-
end:
return;
}
-static void vivi_fillbuff(struct vivi_dev *dev,struct vivi_buffer *buf)
+static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
{
- int h,pos=0;
+ int h , pos = 0;
int hmax = buf->vb.height;
int wmax = buf->vb.width;
struct timeval ts;
- char *tmpbuf = kmalloc(wmax*2,GFP_KERNEL);
- void *vbuf=videobuf_to_vmalloc (&buf->vb);
- /* FIXME: move to dev struct */
- static int mv_count=0;
+ char *tmpbuf = kmalloc(wmax * 2, GFP_KERNEL);
+ void *vbuf = videobuf_to_vmalloc(&buf->vb);
if (!tmpbuf)
return;
- for (h=0;h<hmax;h++) {
- gen_line(tmpbuf,0,wmax,hmax,h,mv_count,
+ for (h = 0; h < hmax; h++) {
+ gen_line(tmpbuf, 0, wmax, hmax, h, dev->mv_count,
dev->timestr);
/* FIXME: replacing to __copy_to_user */
- if (copy_to_user(vbuf+pos,tmpbuf,wmax*2)!=0)
- dprintk(2,"vivifill copy_to_user failed.\n");
+ if (copy_to_user(vbuf + pos, tmpbuf, wmax * 2) != 0)
+ dprintk(dev, 2, "vivifill copy_to_user failed.\n");
pos += wmax*2;
}
- mv_count++;
+ dev->mv_count++;
kfree(tmpbuf);
/* Updates stream time */
- dev->us+=jiffies_to_usecs(jiffies-dev->jiffies);
- dev->jiffies=jiffies;
- if (dev->us>=1000000) {
- dev->us-=1000000;
+ dev->ms += jiffies_to_msecs(jiffies-dev->jiffies);
+ dev->jiffies = jiffies;
+ if (dev->ms >= 1000) {
+ dev->ms -= 1000;
dev->s++;
- if (dev->s>=60) {
- dev->s-=60;
+ if (dev->s >= 60) {
+ dev->s -= 60;
dev->m++;
- if (dev->m>60) {
- dev->m-=60;
+ if (dev->m > 60) {
+ dev->m -= 60;
dev->h++;
- if (dev->h>24)
- dev->h-=24;
+ if (dev->h > 24)
+ dev->h -= 24;
}
}
}
- sprintf(dev->timestr,"%02d:%02d:%02d:%03d",
- dev->h,dev->m,dev->s,(dev->us+500)/1000);
+ sprintf(dev->timestr, "%02d:%02d:%02d:%03d",
+ dev->h, dev->m, dev->s, dev->ms);
- dprintk(2,"vivifill at %s: Buffer 0x%08lx size= %d\n",dev->timestr,
- (unsigned long)tmpbuf,pos);
+ dprintk(dev, 2, "vivifill at %s: Buffer 0x%08lx size= %d\n",
+ dev->timestr, (unsigned long)tmpbuf, pos);
/* Advice that buffer was filled */
- buf->vb.state = STATE_DONE;
+ buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
do_gettimeofday(&ts);
buf->vb.ts = ts;
@@ -384,14 +386,15 @@ static int restart_video_queue(struct vivi_dmaqueue *dma_q);
static void vivi_thread_tick(struct vivi_dmaqueue *dma_q)
{
struct vivi_buffer *buf;
- struct vivi_dev *dev= container_of(dma_q,struct vivi_dev,vidq);
+ struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq);
int bc;
+ spin_lock(&dev->slock);
/* Announces videobuf that all went ok */
for (bc = 0;; bc++) {
if (list_empty(&dma_q->active)) {
- dprintk(1,"No active queue to serve\n");
+ dprintk(dev, 1, "No active queue to serve\n");
break;
}
@@ -401,65 +404,89 @@ static void vivi_thread_tick(struct vivi_dmaqueue *dma_q)
/* Nobody is waiting something to be done, just return */
if (!waitqueue_active(&buf->vb.done)) {
mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT);
+ spin_unlock(&dev->slock);
return;
}
do_gettimeofday(&buf->vb.ts);
- dprintk(2,"[%p/%d] wakeup\n",buf,buf->vb.i);
+ dprintk(dev, 2, "[%p/%d] wakeup\n", buf, buf->vb. i);
/* Fill buffer */
- vivi_fillbuff(dev,buf);
+ vivi_fillbuff(dev, buf);
if (list_empty(&dma_q->active)) {
del_timer(&dma_q->timeout);
} else {
- mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT);
+ mod_timer(&dma_q->timeout, jiffies + BUFFER_TIMEOUT);
}
}
if (bc != 1)
- dprintk(1,"%s: %d buffers handled (should be 1)\n",__FUNCTION__,bc);
+ dprintk(dev, 1, "%s: %d buffers handled (should be 1)\n",
+ __FUNCTION__, bc);
+ spin_unlock(&dev->slock);
}
+#define frames_to_ms(frames) \
+ ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR)
+
static void vivi_sleep(struct vivi_dmaqueue *dma_q)
{
- int timeout;
+ struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq);
+ int timeout, running_time;
DECLARE_WAITQUEUE(wait, current);
- dprintk(1,"%s dma_q=0x%08lx\n",__FUNCTION__,(unsigned long)dma_q);
+ dprintk(dev, 1, "%s dma_q=0x%08lx\n", __FUNCTION__,
+ (unsigned long)dma_q);
add_wait_queue(&dma_q->wq, &wait);
- if (!kthread_should_stop()) {
- dma_q->frame++;
+ if (kthread_should_stop())
+ goto stop_task;
- /* Calculate time to wake up */
- timeout=dma_q->ini_jiffies+msecs_to_jiffies((dma_q->frame*WAKE_NUMERATOR*1000)/WAKE_DENOMINATOR)-jiffies;
+ running_time = jiffies - dma_q->ini_jiffies;
+ dma_q->frame++;
- if (timeout <= 0) {
- int old=dma_q->frame;
- dma_q->frame=(jiffies_to_msecs(jiffies-dma_q->ini_jiffies)*WAKE_DENOMINATOR)/(WAKE_NUMERATOR*1000)+1;
+ /* Calculate time to wake up */
+ timeout = msecs_to_jiffies(frames_to_ms(dma_q->frame)) - running_time;
- timeout=dma_q->ini_jiffies+msecs_to_jiffies((dma_q->frame*WAKE_NUMERATOR*1000)/WAKE_DENOMINATOR)-jiffies;
+ if (timeout > msecs_to_jiffies(frames_to_ms(2)) || timeout <= 0) {
+ int old = dma_q->frame;
+ int nframes;
- dprintk(1,"underrun, losed %d frames. "
- "Now, frame is %d. Waking on %d jiffies\n",
- dma_q->frame-old,dma_q->frame,timeout);
- } else
- dprintk(1,"will sleep for %i jiffies\n",timeout);
+ dma_q->frame = (jiffies_to_msecs(running_time) /
+ frames_to_ms(1)) + 1;
- vivi_thread_tick(dma_q);
+ timeout = msecs_to_jiffies(frames_to_ms(dma_q->frame))
+ - running_time;
- schedule_timeout_interruptible (timeout);
- }
+ if (unlikely (timeout <= 0))
+ timeout = 1;
+
+ nframes = (dma_q->frame > old)?
+ dma_q->frame - old : old - dma_q->frame;
+
+ dprintk(dev, 1, "%ld: %s %d frames. "
+ "Current frame is %d. Will sleep for %d jiffies\n",
+ jiffies,
+ (dma_q->frame > old)? "Underrun, losed" : "Overrun of",
+ nframes, dma_q->frame, timeout);
+ } else
+ dprintk(dev, 1, "will sleep for %d jiffies\n", timeout);
+ vivi_thread_tick(dma_q);
+
+ schedule_timeout_interruptible(timeout);
+
+stop_task:
remove_wait_queue(&dma_q->wq, &wait);
try_to_freeze();
}
static int vivi_thread(void *data)
{
- struct vivi_dmaqueue *dma_q=data;
+ struct vivi_dmaqueue *dma_q = data;
+ struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq);
- dprintk(1,"thread started\n");
+ dprintk(dev, 1, "thread started\n");
mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT);
set_freezable();
@@ -470,16 +497,18 @@ static int vivi_thread(void *data)
if (kthread_should_stop())
break;
}
- dprintk(1, "thread: exit\n");
+ dprintk(dev, 1, "thread: exit\n");
return 0;
}
static int vivi_start_thread(struct vivi_dmaqueue *dma_q)
{
- dma_q->frame=0;
- dma_q->ini_jiffies=jiffies;
+ struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq);
- dprintk(1,"%s\n",__FUNCTION__);
+ dma_q->frame = 0;
+ dma_q->ini_jiffies = jiffies;
+
+ dprintk(dev, 1, "%s\n", __FUNCTION__);
dma_q->kthread = kthread_run(vivi_thread, dma_q, "vivi");
@@ -490,39 +519,43 @@ static int vivi_start_thread(struct vivi_dmaqueue *dma_q)
/* Wakes thread */
wake_up_interruptible(&dma_q->wq);
- dprintk(1,"returning from %s\n",__FUNCTION__);
+ dprintk(dev, 1, "returning from %s\n", __FUNCTION__);
return 0;
}
static void vivi_stop_thread(struct vivi_dmaqueue *dma_q)
{
- dprintk(1,"%s\n",__FUNCTION__);
+ struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq);
+
+ dprintk(dev, 1, "%s\n", __FUNCTION__);
/* shutdown control thread */
if (dma_q->kthread) {
kthread_stop(dma_q->kthread);
- dma_q->kthread=NULL;
+ dma_q->kthread = NULL;
}
}
static int restart_video_queue(struct vivi_dmaqueue *dma_q)
{
+ struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq);
struct vivi_buffer *buf, *prev;
- dprintk(1,"%s dma_q=0x%08lx\n",__FUNCTION__,(unsigned long)dma_q);
+ dprintk(dev, 1, "%s dma_q=0x%08lx\n", __FUNCTION__,
+ (unsigned long)dma_q);
if (!list_empty(&dma_q->active)) {
- buf = list_entry(dma_q->active.next, struct vivi_buffer, vb.queue);
- dprintk(2,"restart_queue [%p/%d]: restart dma\n",
+ buf = list_entry(dma_q->active.next,
+ struct vivi_buffer, vb.queue);
+ dprintk(dev, 2, "restart_queue [%p/%d]: restart dma\n",
buf, buf->vb.i);
- dprintk(1,"Restarting video dma\n");
+ dprintk(dev, 1, "Restarting video dma\n");
vivi_stop_thread(dma_q);
-// vivi_start_thread(dma_q);
/* cancel all outstanding capture / vbi requests */
list_for_each_entry_safe(buf, prev, &dma_q->active, vb.queue) {
list_del(&buf->vb.queue);
- buf->vb.state = STATE_ERROR;
+ buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
}
mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT);
@@ -534,28 +567,31 @@ static int restart_video_queue(struct vivi_dmaqueue *dma_q)
for (;;) {
if (list_empty(&dma_q->queued))
return 0;
- buf = list_entry(dma_q->queued.next, struct vivi_buffer, vb.queue);
+ buf = list_entry(dma_q->queued.next,
+ struct vivi_buffer, vb.queue);
if (NULL == prev) {
list_del(&buf->vb.queue);
- list_add_tail(&buf->vb.queue,&dma_q->active);
+ list_add_tail(&buf->vb.queue, &dma_q->active);
- dprintk(1,"Restarting video dma\n");
+ dprintk(dev, 1, "Restarting video dma\n");
vivi_stop_thread(dma_q);
vivi_start_thread(dma_q);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT);
- dprintk(2,"[%p/%d] restart_queue - first active\n",
- buf,buf->vb.i);
+ dprintk(dev, 2,
+ "[%p/%d] restart_queue - first active\n",
+ buf, buf->vb.i);
} else if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
list_del(&buf->vb.queue);
- list_add_tail(&buf->vb.queue,&dma_q->active);
- buf->vb.state = STATE_ACTIVE;
- dprintk(2,"[%p/%d] restart_queue - move to active\n",
- buf,buf->vb.i);
+ list_add_tail(&buf->vb.queue, &dma_q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ dprintk(dev, 2,
+ "[%p/%d] restart_queue - move to active\n",
+ buf, buf->vb.i);
} else {
return 0;
}
@@ -565,19 +601,23 @@ static int restart_video_queue(struct vivi_dmaqueue *dma_q)
static void vivi_vid_timeout(unsigned long data)
{
- struct vivi_dev *dev = (struct vivi_dev*)data;
+ struct vivi_dev *dev = (struct vivi_dev *)data;
struct vivi_dmaqueue *vidq = &dev->vidq;
struct vivi_buffer *buf;
+ spin_lock(&dev->slock);
+
while (!list_empty(&vidq->active)) {
- buf = list_entry(vidq->active.next, struct vivi_buffer, vb.queue);
+ buf = list_entry(vidq->active.next,
+ struct vivi_buffer, vb.queue);
list_del(&buf->vb.queue);
- buf->vb.state = STATE_ERROR;
+ buf->vb.state = VIDEOBUF_ERROR;
wake_up(&buf->vb.done);
- printk("vivi/0: [%p/%d] timeout\n", buf, buf->vb.i);
+ printk(KERN_INFO "vivi/0: [%p/%d] timeout\n", buf, buf->vb.i);
}
-
restart_video_queue(vidq);
+
+ spin_unlock(&dev->slock);
}
/* ------------------------------------------------------------------
@@ -586,7 +626,8 @@ static void vivi_vid_timeout(unsigned long data)
static int
buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
{
- struct vivi_fh *fh = vq->priv_data;
+ struct vivi_fh *fh = vq->priv_data;
+ struct vivi_dev *dev = fh->dev;
*size = fh->width*fh->height*2;
@@ -596,21 +637,25 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
while (*size * *count > vid_limit * 1024 * 1024)
(*count)--;
- dprintk(1,"%s, count=%d, size=%d\n",__FUNCTION__,*count, *size);
+ dprintk(dev, 1, "%s, count=%d, size=%d\n", __FUNCTION__,
+ *count, *size);
return 0;
}
static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf)
{
- dprintk(1,"%s\n",__FUNCTION__);
+ struct vivi_fh *fh = vq->priv_data;
+ struct vivi_dev *dev = fh->dev;
+
+ dprintk(dev, 1, "%s\n", __FUNCTION__);
if (in_interrupt())
BUG();
- videobuf_waiton(&buf->vb,0,0);
+ videobuf_waiton(&buf->vb, 0, 0);
videobuf_vmalloc_free(&buf->vb);
- buf->vb.state = STATE_NEEDS_INIT;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
#define norm_maxw() 1024
@@ -620,10 +665,11 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
enum v4l2_field field)
{
struct vivi_fh *fh = vq->priv_data;
- struct vivi_buffer *buf = container_of(vb,struct vivi_buffer,vb);
+ struct vivi_dev *dev = fh->dev;
+ struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
int rc, init_buffer = 0;
- dprintk(1,"%s, field=%d\n",__FUNCTION__,field);
+ dprintk(dev, 1, "%s, field=%d\n", __FUNCTION__, field);
BUG_ON(NULL == fh->fmt);
if (fh->width < 48 || fh->width > norm_maxw() ||
@@ -644,75 +690,81 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
init_buffer = 1;
}
- if (STATE_NEEDS_INIT == buf->vb.state) {
- if (0 != (rc = videobuf_iolock(vq,&buf->vb,NULL)))
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0)
goto fail;
}
- buf->vb.state = STATE_PREPARED;
+ buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
- free_buffer(vq,buf);
+ free_buffer(vq, buf);
return rc;
}
static void
buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
- struct vivi_buffer *buf = container_of(vb,struct vivi_buffer,vb);
- struct vivi_fh *fh = vq->priv_data;
- struct vivi_dev *dev = fh->dev;
- struct vivi_dmaqueue *vidq = &dev->vidq;
+ struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
+ struct vivi_fh *fh = vq->priv_data;
+ struct vivi_dev *dev = fh->dev;
+ struct vivi_dmaqueue *vidq = &dev->vidq;
struct vivi_buffer *prev;
if (!list_empty(&vidq->queued)) {
- dprintk(1,"adding vb queue=0x%08lx\n",(unsigned long)&buf->vb.queue);
- list_add_tail(&buf->vb.queue,&vidq->queued);
- buf->vb.state = STATE_QUEUED;
- dprintk(2,"[%p/%d] buffer_queue - append to queued\n",
+ dprintk(dev, 1, "adding vb queue=0x%08lx\n",
+ (unsigned long)&buf->vb.queue);
+ list_add_tail(&buf->vb.queue, &vidq->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(dev, 2, "[%p/%d] buffer_queue - append to queued\n",
buf, buf->vb.i);
} else if (list_empty(&vidq->active)) {
- list_add_tail(&buf->vb.queue,&vidq->active);
+ list_add_tail(&buf->vb.queue, &vidq->active);
- buf->vb.state = STATE_ACTIVE;
+ buf->vb.state = VIDEOBUF_ACTIVE;
mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT);
- dprintk(2,"[%p/%d] buffer_queue - first active\n",
+ dprintk(dev, 2, "[%p/%d] buffer_queue - first active\n",
buf, buf->vb.i);
vivi_start_thread(vidq);
} else {
- prev = list_entry(vidq->active.prev, struct vivi_buffer, vb.queue);
+ prev = list_entry(vidq->active.prev,
+ struct vivi_buffer, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
- list_add_tail(&buf->vb.queue,&vidq->active);
- buf->vb.state = STATE_ACTIVE;
- dprintk(2,"[%p/%d] buffer_queue - append to active\n",
+ list_add_tail(&buf->vb.queue, &vidq->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ dprintk(dev, 2,
+ "[%p/%d] buffer_queue - append to active\n",
buf, buf->vb.i);
} else {
- list_add_tail(&buf->vb.queue,&vidq->queued);
- buf->vb.state = STATE_QUEUED;
- dprintk(2,"[%p/%d] buffer_queue - first queued\n",
+ list_add_tail(&buf->vb.queue, &vidq->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(dev, 2,
+ "[%p/%d] buffer_queue - first queued\n",
buf, buf->vb.i);
}
}
}
-static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
{
- struct vivi_buffer *buf = container_of(vb,struct vivi_buffer,vb);
+ struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
struct vivi_fh *fh = vq->priv_data;
- struct vivi_dev *dev = (struct vivi_dev*)fh->dev;
+ struct vivi_dev *dev = (struct vivi_dev *)fh->dev;
struct vivi_dmaqueue *vidq = &dev->vidq;
- dprintk(1,"%s\n",__FUNCTION__);
+ dprintk(dev, 1, "%s\n", __FUNCTION__);
vivi_stop_thread(vidq);
- free_buffer(vq,buf);
+ free_buffer(vq, buf);
}
static struct videobuf_queue_ops vivi_video_qops = {
@@ -725,7 +777,7 @@ static struct videobuf_queue_ops vivi_video_qops = {
/* ------------------------------------------------------------------
IOCTL vidioc handling
------------------------------------------------------------------*/
-static int vidioc_querycap (struct file *file, void *priv,
+static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
strcpy(cap->driver, "vivi");
@@ -737,21 +789,21 @@ static int vidioc_querycap (struct file *file, void *priv,
return 0;
}
-static int vidioc_enum_fmt_cap (struct file *file, void *priv,
+static int vidioc_enum_fmt_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index > 0)
return -EINVAL;
- strlcpy(f->description,format.name,sizeof(f->description));
+ strlcpy(f->description, format.name, sizeof(f->description));
f->pixelformat = format.fourcc;
return 0;
}
-static int vidioc_g_fmt_cap (struct file *file, void *priv,
+static int vidioc_g_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct vivi_fh *fh=priv;
+ struct vivi_fh *fh = priv;
f->fmt.pix.width = fh->width;
f->fmt.pix.height = fh->height;
@@ -765,26 +817,29 @@ static int vidioc_g_fmt_cap (struct file *file, void *priv,
return (0);
}
-static int vidioc_try_fmt_cap (struct file *file, void *priv,
+static int vidioc_try_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
+ struct vivi_fh *fh = priv;
+ struct vivi_dev *dev = fh->dev;
struct vivi_fmt *fmt;
enum v4l2_field field;
unsigned int maxw, maxh;
if (format.fourcc != f->fmt.pix.pixelformat) {
- dprintk(1,"Fourcc format (0x%08x) invalid. Driver accepts "
- "only 0x%08x\n",f->fmt.pix.pixelformat,format.fourcc);
+ dprintk(dev, 1, "Fourcc format (0x%08x) invalid. "
+ "Driver accepts only 0x%08x\n",
+ f->fmt.pix.pixelformat, format.fourcc);
return -EINVAL;
}
- fmt=&format;
+ fmt = &format;
field = f->fmt.pix.field;
if (field == V4L2_FIELD_ANY) {
- field=V4L2_FIELD_INTERLACED;
+ field = V4L2_FIELD_INTERLACED;
} else if (V4L2_FIELD_INTERLACED != field) {
- dprintk(1,"Field type invalid.\n");
+ dprintk(dev, 1, "Field type invalid.\n");
return -EINVAL;
}
@@ -810,11 +865,11 @@ static int vidioc_try_fmt_cap (struct file *file, void *priv,
}
/*FIXME: This seems to be generic enough to be at videodev2 */
-static int vidioc_s_fmt_cap (struct file *file, void *priv,
+static int vidioc_s_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct vivi_fh *fh=priv;
- int ret = vidioc_try_fmt_cap(file,fh,f);
+ struct vivi_fh *fh = priv;
+ int ret = vidioc_try_fmt_cap(file, fh, f);
if (ret < 0)
return (ret);
@@ -827,47 +882,48 @@ static int vidioc_s_fmt_cap (struct file *file, void *priv,
return (0);
}
-static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
{
- struct vivi_fh *fh=priv;
+ struct vivi_fh *fh = priv;
return (videobuf_reqbufs(&fh->vb_vidq, p));
}
-static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
+static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
- struct vivi_fh *fh=priv;
+ struct vivi_fh *fh = priv;
return (videobuf_querybuf(&fh->vb_vidq, p));
}
-static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
- struct vivi_fh *fh=priv;
+ struct vivi_fh *fh = priv;
return (videobuf_qbuf(&fh->vb_vidq, p));
}
-static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
- struct vivi_fh *fh=priv;
+ struct vivi_fh *fh = priv;
return (videobuf_dqbuf(&fh->vb_vidq, p,
file->f_flags & O_NONBLOCK));
}
#ifdef CONFIG_VIDEO_V4L1_COMPAT
-static int vidiocgmbuf (struct file *file, void *priv, struct video_mbuf *mbuf)
+static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
{
- struct vivi_fh *fh=priv;
+ struct vivi_fh *fh = priv;
- return videobuf_cgmbuf (&fh->vb_vidq, mbuf, 8);
+ return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
}
#endif
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
- struct vivi_fh *fh=priv;
+ struct vivi_fh *fh = priv;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -879,7 +935,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
- struct vivi_fh *fh=priv;
+ struct vivi_fh *fh = priv;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -889,32 +945,32 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
return videobuf_streamoff(&fh->vb_vidq);
}
-static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *i)
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
{
return 0;
}
/* only one input in this sample driver */
-static int vidioc_enum_input (struct file *file, void *priv,
+static int vidioc_enum_input(struct file *file, void *priv,
struct v4l2_input *inp)
{
if (inp->index != 0)
return -EINVAL;
inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = V4L2_STD_NTSC_M;
- strcpy(inp->name,"Camera");
+ inp->std = V4L2_STD_525_60;
+ strcpy(inp->name, "Camera");
return (0);
}
-static int vidioc_g_input (struct file *file, void *priv, unsigned int *i)
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
{
*i = 0;
return (0);
}
-static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
if (i > 0)
return -EINVAL;
@@ -923,8 +979,8 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
}
/* --- controls ---------------------------------------------- */
-static int vidioc_queryctrl (struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
{
int i;
@@ -938,33 +994,31 @@ static int vidioc_queryctrl (struct file *file, void *priv,
return -EINVAL;
}
-static int vidioc_g_ctrl (struct file *file, void *priv,
- struct v4l2_control *ctrl)
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
{
int i;
for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++)
if (ctrl->id == vivi_qctrl[i].id) {
- ctrl->value=qctl_regs[i];
+ ctrl->value = qctl_regs[i];
return (0);
}
return -EINVAL;
}
-static int vidioc_s_ctrl (struct file *file, void *priv,
+static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
int i;
for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++)
if (ctrl->id == vivi_qctrl[i].id) {
- if (ctrl->value <
- vivi_qctrl[i].minimum
- || ctrl->value >
- vivi_qctrl[i].maximum) {
+ if (ctrl->value < vivi_qctrl[i].minimum
+ || ctrl->value > vivi_qctrl[i].maximum) {
return (-ERANGE);
}
- qctl_regs[i]=ctrl->value;
+ qctl_regs[i] = ctrl->value;
return (0);
}
return -EINVAL;
@@ -983,24 +1037,22 @@ static int vivi_open(struct inode *inode, struct file *file)
struct vivi_fh *fh;
int i;
- printk(KERN_DEBUG "vivi: open called (minor=%d)\n",minor);
+ printk(KERN_DEBUG "vivi: open called (minor=%d)\n", minor);
list_for_each_entry(dev, &vivi_devlist, vivi_devlist)
- if (dev->vfd.minor == minor)
+ if (dev->vfd->minor == minor)
goto found;
return -ENODEV;
-found:
-
-
+found:
/* If more than one user, mutex should be added */
dev->users++;
- dprintk(1, "open minor=%d type=%s users=%d\n", minor,
+ dprintk(dev, 1, "open minor=%d type=%s users=%d\n", minor,
v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users);
/* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
dev->users--;
return -ENOMEM;
@@ -1016,27 +1068,21 @@ found:
/* Put all controls at a sane state */
for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++)
- qctl_regs[i] =vivi_qctrl[i].default_value;
-
- dprintk(1,"Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n",
- (unsigned long)fh,(unsigned long)dev,(unsigned long)&dev->vidq);
- dprintk(1,"Open: list_empty queued=%d\n",list_empty(&dev->vidq.queued));
- dprintk(1,"Open: list_empty active=%d\n",list_empty(&dev->vidq.active));
+ qctl_regs[i] = vivi_qctrl[i].default_value;
/* Resets frame counters */
- dev->h=0;
- dev->m=0;
- dev->s=0;
- dev->us=0;
- dev->jiffies=jiffies;
- sprintf(dev->timestr,"%02d:%02d:%02d:%03d",
- dev->h,dev->m,dev->s,(dev->us+500)/1000);
+ dev->h = 0;
+ dev->m = 0;
+ dev->s = 0;
+ dev->ms = 0;
+ dev->mv_count = 0;
+ dev->jiffies = jiffies;
+ sprintf(dev->timestr, "%02d:%02d:%02d:%03d",
+ dev->h, dev->m, dev->s, dev->ms);
videobuf_queue_vmalloc_init(&fh->vb_vidq, &vivi_video_qops,
- NULL, NULL,
- fh->type,
- V4L2_FIELD_INTERLACED,
- sizeof(struct vivi_buffer),fh);
+ NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED,
+ sizeof(struct vivi_buffer), fh);
return 0;
}
@@ -1044,9 +1090,9 @@ found:
static ssize_t
vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
- struct vivi_fh *fh = file->private_data;
+ struct vivi_fh *fh = file->private_data;
- if (fh->type==V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0,
file->f_flags & O_NONBLOCK);
}
@@ -1057,9 +1103,10 @@ static unsigned int
vivi_poll(struct file *file, struct poll_table_struct *wait)
{
struct vivi_fh *fh = file->private_data;
+ struct vivi_dev *dev = fh->dev;
struct videobuf_queue *q = &fh->vb_vidq;
- dprintk(1,"%s\n",__FUNCTION__);
+ dprintk(dev, 1, "%s\n", __FUNCTION__);
if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
return POLLERR;
@@ -1067,7 +1114,7 @@ vivi_poll(struct file *file, struct poll_table_struct *wait)
return videobuf_poll_stream(file, q, wait);
}
-static int vivi_release(struct inode *inode, struct file *file)
+static int vivi_close(struct inode *inode, struct file *file)
{
struct vivi_fh *fh = file->private_data;
struct vivi_dev *dev = fh->dev;
@@ -1079,26 +1126,48 @@ static int vivi_release(struct inode *inode, struct file *file)
videobuf_stop(&fh->vb_vidq);
videobuf_mmap_free(&fh->vb_vidq);
- kfree (fh);
+ kfree(fh);
dev->users--;
- printk(KERN_DEBUG "vivi: close called (minor=%d, users=%d)\n",minor,dev->users);
+ dprintk(dev, 1, "close called (minor=%d, users=%d)\n",
+ minor, dev->users);
return 0;
}
-static int
-vivi_mmap(struct file *file, struct vm_area_struct * vma)
+static int vivi_release(void)
{
- struct vivi_fh *fh = file->private_data;
+ struct vivi_dev *dev;
+ struct list_head *list;
+
+ while (!list_empty(&vivi_devlist)) {
+ list = vivi_devlist.next;
+ list_del(list);
+ dev = list_entry(list, struct vivi_dev, vivi_devlist);
+
+ if (-1 != dev->vfd->minor)
+ video_unregister_device(dev->vfd);
+ else
+ video_device_release(dev->vfd);
+
+ kfree(dev);
+ }
+
+ return 0;
+}
+
+static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct vivi_fh *fh = file->private_data;
+ struct vivi_dev *dev = fh->dev;
int ret;
- dprintk (1,"mmap called, vma=0x%08lx\n",(unsigned long)vma);
+ dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
- ret=videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
- dprintk (1,"vma start=0x%08lx, size=%ld, ret=%d\n",
+ dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n",
(unsigned long)vma->vm_start,
(unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
ret);
@@ -1109,7 +1178,7 @@ vivi_mmap(struct file *file, struct vm_area_struct * vma)
static const struct file_operations vivi_fops = {
.owner = THIS_MODULE,
.open = vivi_open,
- .release = vivi_release,
+ .release = vivi_close,
.read = vivi_read,
.poll = vivi_poll,
.ioctl = video_ioctl2, /* V4L2 ioctl handler */
@@ -1117,12 +1186,12 @@ static const struct file_operations vivi_fops = {
.llseek = no_llseek,
};
-static struct video_device vivi = {
+static struct video_device vivi_template = {
.name = "vivi",
.type = VID_TYPE_CAPTURE,
.fops = &vivi_fops,
.minor = -1,
-// .release = video_device_release,
+ .release = video_device_release,
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_cap = vidioc_enum_fmt_cap,
@@ -1145,7 +1214,7 @@ static struct video_device vivi = {
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
- .tvnorms = V4L2_STD_NTSC_M,
+ .tvnorms = V4L2_STD_525_60,
.current_norm = V4L2_STD_NTSC_M,
};
/* -----------------------------------------------------------------
@@ -1154,43 +1223,61 @@ static struct video_device vivi = {
static int __init vivi_init(void)
{
- int ret;
+ int ret = -ENOMEM, i;
struct vivi_dev *dev;
+ struct video_device *vfd;
- dev = kzalloc(sizeof(*dev),GFP_KERNEL);
- if (NULL == dev)
- return -ENOMEM;
- list_add_tail(&dev->vivi_devlist,&vivi_devlist);
+ for (i = 0; i < n_devs; i++) {
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (NULL == dev)
+ break;
- /* init video dma queues */
- INIT_LIST_HEAD(&dev->vidq.active);
- INIT_LIST_HEAD(&dev->vidq.queued);
- init_waitqueue_head(&dev->vidq.wq);
+ list_add_tail(&dev->vivi_devlist, &vivi_devlist);
- /* initialize locks */
- mutex_init(&dev->lock);
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+ init_waitqueue_head(&dev->vidq.wq);
- dev->vidq.timeout.function = vivi_vid_timeout;
- dev->vidq.timeout.data = (unsigned long)dev;
- init_timer(&dev->vidq.timeout);
+ /* initialize locks */
+ mutex_init(&dev->lock);
+ spin_lock_init(&dev->slock);
- ret = video_register_device(&vivi, VFL_TYPE_GRABBER, video_nr);
- printk(KERN_INFO "Video Technology Magazine Virtual Video Capture Board (Load status: %d)\n", ret);
+ dev->vidq.timeout.function = vivi_vid_timeout;
+ dev->vidq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vidq.timeout);
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ break;
+
+ *vfd = vivi_template;
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
+ if (ret < 0)
+ break;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s (%i)",
+ vivi_template.name, vfd->minor);
+
+ if (video_nr >= 0)
+ video_nr++;
+
+ dev->vfd = vfd;
+ }
+
+ if (ret < 0) {
+ vivi_release();
+ printk(KERN_INFO "Error %d while loading vivi driver\n", ret);
+ } else
+ printk(KERN_INFO "Video Technology Magazine Virtual Video "
+ "Capture Board successfully loaded.\n");
return ret;
}
static void __exit vivi_exit(void)
{
- struct vivi_dev *h;
- struct list_head *list;
-
- while (!list_empty(&vivi_devlist)) {
- list = vivi_devlist.next;
- list_del(list);
- h = list_entry(list, struct vivi_dev, vivi_devlist);
- kfree (h);
- }
- video_unregister_device(&vivi);
+ vivi_release();
}
module_init(vivi_init);
@@ -1201,10 +1288,13 @@ MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol");
MODULE_LICENSE("Dual BSD/GPL");
module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr, "video iminor start number");
-module_param_named(debug,vivi.debug, int, 0644);
-MODULE_PARM_DESC(debug,"activates debug info");
+module_param(n_devs, int, 0);
+MODULE_PARM_DESC(n_devs, "number of video devices to create");
-module_param(vid_limit,int,0644);
-MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes");
+module_param_named(debug, vivi_template.debug, int, 0444);
+MODULE_PARM_DESC(debug, "activates debug info");
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c
index 63002e0ac764..282c81403c97 100644
--- a/drivers/media/video/vp27smpx.c
+++ b/drivers/media/video/vp27smpx.c
@@ -30,15 +30,12 @@
#include <linux/videodev.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("vp27smpx driver");
MODULE_AUTHOR("Hans Verkuil");
MODULE_LICENSE("GPL");
-static unsigned short normal_i2c[] = { 0xb6 >> 1, I2C_CLIENT_END };
-
-
-I2C_CLIENT_INSMOD;
/* ----------------------------------------------------------------------- */
@@ -53,28 +50,26 @@ static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode)
u8 data[3] = { 0x00, 0x00, 0x04 };
switch (audmode) {
- case V4L2_TUNER_MODE_MONO:
- case V4L2_TUNER_MODE_LANG1:
- break;
- case V4L2_TUNER_MODE_STEREO:
- case V4L2_TUNER_MODE_LANG1_LANG2:
- data[1] = 0x01;
- break;
- case V4L2_TUNER_MODE_LANG2:
- data[1] = 0x02;
- break;
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_LANG1:
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ data[1] = 0x01;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ data[1] = 0x02;
+ break;
}
- if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) {
- v4l_err(client, "%s: I/O error setting audmode\n", client->name);
- }
- else {
+ if (i2c_master_send(client, data, sizeof(data)) != sizeof(data))
+ v4l_err(client, "%s: I/O error setting audmode\n",
+ client->name);
+ else
state->audmode = audmode;
- }
}
-static int vp27smpx_command(struct i2c_client *client, unsigned int cmd,
- void *arg)
+static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg)
{
struct vp27smpx_state *state = i2c_get_clientdata(client);
struct v4l2_tuner *vt = arg;
@@ -103,7 +98,8 @@ static int vp27smpx_command(struct i2c_client *client, unsigned int cmd,
break;
case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_VP27SMPX, 0);
+ return v4l2_chip_ident_i2c_client(client, arg,
+ V4L2_IDENT_VP27SMPX, 0);
case VIDIOC_LOG_STATUS:
v4l_info(client, "Audio Mode: %u%s\n", state->audmode,
@@ -125,88 +121,43 @@ static int vp27smpx_command(struct i2c_client *client, unsigned int cmd,
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
-static struct i2c_driver i2c_driver;
-
-static int vp27smpx_attach(struct i2c_adapter *adapter, int address, int kind)
+static int vp27smpx_probe(struct i2c_client *client)
{
- struct i2c_client *client;
struct vp27smpx_state *state;
/* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return 0;
-
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == 0)
- return -ENOMEM;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver;
snprintf(client->name, sizeof(client->name) - 1, "vp27smpx");
- v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name);
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL);
- if (state == NULL) {
- kfree(client);
+ if (state == NULL)
return -ENOMEM;
- }
state->audmode = V4L2_TUNER_MODE_STEREO;
i2c_set_clientdata(client, state);
/* initialize vp27smpx */
vp27smpx_set_audmode(client, state->audmode);
- i2c_attach_client(client);
-
return 0;
}
-static int vp27smpx_probe(struct i2c_adapter *adapter)
+static int vp27smpx_remove(struct i2c_client *client)
{
- if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, vp27smpx_attach);
- return 0;
-}
-
-static int vp27smpx_detach(struct i2c_client *client)
-{
- struct vp27smpx_state *state = i2c_get_clientdata(client);
- int err;
-
- err = i2c_detach_client(client);
- if (err) {
- return err;
- }
- kfree(state);
- kfree(client);
-
+ kfree(i2c_get_clientdata(client));
return 0;
}
/* ----------------------------------------------------------------------- */
-/* i2c implementation */
-static struct i2c_driver i2c_driver = {
- .driver = {
- .name = "vp27smpx",
- },
- .id = I2C_DRIVERID_VP27SMPX,
- .attach_adapter = vp27smpx_probe,
- .detach_client = vp27smpx_detach,
- .command = vp27smpx_command,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "vp27smpx",
+ .driverid = I2C_DRIVERID_VP27SMPX,
+ .command = vp27smpx_command,
+ .probe = vp27smpx_probe,
+ .remove = vp27smpx_remove,
};
-
-static int __init vp27smpx_init_module(void)
-{
- return i2c_add_driver(&i2c_driver);
-}
-
-static void __exit vp27smpx_cleanup_module(void)
-{
- i2c_del_driver(&i2c_driver);
-}
-
-module_init(vp27smpx_init_module);
-module_exit(vp27smpx_cleanup_module);
diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c
index 1bf4cbec6a87..31795b4f8b63 100644
--- a/drivers/media/video/wm8739.c
+++ b/drivers/media/video/wm8739.c
@@ -30,21 +30,19 @@
#include <linux/videodev.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("wm8739 driver");
MODULE_AUTHOR("T. Adachi, Hans Verkuil");
MODULE_LICENSE("GPL");
-static int debug = 0;
-static unsigned short normal_i2c[] = { 0x34 >> 1, 0x36 >> 1, I2C_CLIENT_END };
+static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
-I2C_CLIENT_INSMOD;
-
/* ------------------------------------------------------------------------ */
enum {
@@ -75,12 +73,10 @@ static int wm8739_write(struct i2c_client *client, int reg, u16 val)
v4l_dbg(1, debug, client, "write: %02x %02x\n", reg, val);
- for (i = 0; i < 3; i++) {
- if (i2c_smbus_write_byte_data(client, (reg << 1) |
- (val >> 8), val & 0xff) == 0) {
+ for (i = 0; i < 3; i++)
+ if (i2c_smbus_write_byte_data(client,
+ (reg << 1) | (val >> 8), val & 0xff) == 0)
return 0;
- }
- }
v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
return -1;
}
@@ -167,7 +163,7 @@ static struct v4l2_queryctrl wm8739_qctrl[] = {
.default_value = 58880,
.flags = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
- },{
+ }, {
.id = V4L2_CID_AUDIO_MUTE,
.name = "Mute",
.minimum = 0,
@@ -176,7 +172,7 @@ static struct v4l2_queryctrl wm8739_qctrl[] = {
.default_value = 1,
.flags = 0,
.type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
+ }, {
.id = V4L2_CID_AUDIO_BALANCE,
.name = "Balance",
.minimum = 0,
@@ -190,7 +186,7 @@ static struct v4l2_queryctrl wm8739_qctrl[] = {
/* ------------------------------------------------------------------------ */
-static int wm8739_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg)
{
struct wm8739_state *state = i2c_get_clientdata(client);
@@ -200,21 +196,26 @@ static int wm8739_command(struct i2c_client *client, unsigned int cmd, void *arg
u32 audiofreq = *(u32 *)arg;
state->clock_freq = audiofreq;
- wm8739_write(client, R9, 0x000); /* de-activate */
+ /* de-activate */
+ wm8739_write(client, R9, 0x000);
switch (audiofreq) {
case 44100:
- wm8739_write(client, R8, 0x020); /* 256fps, fs=44.1k */
+ /* 256fps, fs=44.1k */
+ wm8739_write(client, R8, 0x020);
break;
case 48000:
- wm8739_write(client, R8, 0x000); /* 256fps, fs=48k */
+ /* 256fps, fs=48k */
+ wm8739_write(client, R8, 0x000);
break;
case 32000:
- wm8739_write(client, R8, 0x018); /* 256fps, fs=32k */
+ /* 256fps, fs=32k */
+ wm8739_write(client, R8, 0x018);
break;
default:
break;
}
- wm8739_write(client, R9, 0x001); /* activate */
+ /* activate */
+ wm8739_write(client, R9, 0x001);
break;
}
@@ -238,7 +239,8 @@ static int wm8739_command(struct i2c_client *client, unsigned int cmd, void *arg
}
case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8739, 0);
+ return v4l2_chip_ident_i2c_client(client,
+ arg, V4L2_IDENT_WM8739, 0);
case VIDIOC_LOG_STATUS:
v4l_info(client, "Frequency: %u Hz\n", state->clock_freq);
@@ -259,27 +261,16 @@ static int wm8739_command(struct i2c_client *client, unsigned int cmd, void *arg
/* i2c implementation */
-static struct i2c_driver i2c_driver;
-
-static int wm8739_attach(struct i2c_adapter *adapter, int address, int kind)
+static int wm8739_probe(struct i2c_client *client)
{
- struct i2c_client *client;
struct wm8739_state *state;
/* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return 0;
-
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == NULL)
- return -ENOMEM;
-
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver;
- snprintf(client->name, sizeof(client->name) - 1, "wm8739");
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
- v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name);
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL);
if (state == NULL) {
@@ -295,67 +286,37 @@ static int wm8739_attach(struct i2c_adapter *adapter, int address, int kind)
state->clock_freq = 48000;
i2c_set_clientdata(client, state);
- /* initialize wm8739 */
- wm8739_write(client, R15, 0x00); /* reset */
- wm8739_write(client, R5, 0x000); /* filter setting, high path, offet clear */
- wm8739_write(client, R6, 0x000); /* ADC, OSC, Power Off mode Disable */
- wm8739_write(client, R7, 0x049); /* Digital Audio interface format */
- /* Enable Master mode */
- /* 24 bit, MSB first/left justified */
- wm8739_write(client, R8, 0x000); /* sampling control */
- /* normal, 256fs, 48KHz sampling rate */
- wm8739_write(client, R9, 0x001); /* activate */
- wm8739_set_audio(client); /* set volume/mute */
-
- i2c_attach_client(client);
-
- return 0;
-}
-
-static int wm8739_probe(struct i2c_adapter *adapter)
-{
- if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, wm8739_attach);
+ /* Initialize wm8739 */
+
+ /* reset */
+ wm8739_write(client, R15, 0x00);
+ /* filter setting, high path, offet clear */
+ wm8739_write(client, R5, 0x000);
+ /* ADC, OSC, Power Off mode Disable */
+ wm8739_write(client, R6, 0x000);
+ /* Digital Audio interface format:
+ Enable Master mode, 24 bit, MSB first/left justified */
+ wm8739_write(client, R7, 0x049);
+ /* sampling control: normal, 256fs, 48KHz sampling rate */
+ wm8739_write(client, R8, 0x000);
+ /* activate */
+ wm8739_write(client, R9, 0x001);
+ /* set volume/mute */
+ wm8739_set_audio(client);
return 0;
}
-static int wm8739_detach(struct i2c_client *client)
+static int wm8739_remove(struct i2c_client *client)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
- int err;
-
- err = i2c_detach_client(client);
- if (err)
- return err;
-
- kfree(state);
- kfree(client);
+ kfree(i2c_get_clientdata(client));
return 0;
}
-/* ----------------------------------------------------------------------- */
-
-/* i2c implementation */
-static struct i2c_driver i2c_driver = {
- .driver = {
- .name = "wm8739",
- },
- .id = I2C_DRIVERID_WM8739,
- .attach_adapter = wm8739_probe,
- .detach_client = wm8739_detach,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "wm8739",
+ .driverid = I2C_DRIVERID_WM8739,
.command = wm8739_command,
+ .probe = wm8739_probe,
+ .remove = wm8739_remove,
};
-
-static int __init wm8739_init_module(void)
-{
- return i2c_add_driver(&i2c_driver);
-}
-
-static void __exit wm8739_cleanup_module(void)
-{
- i2c_del_driver(&i2c_driver);
-}
-
-module_init(wm8739_init_module);
-module_exit(wm8739_cleanup_module);
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
index 9f7e894ef962..869f9e7946b6 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/video/wm8775.c
@@ -34,6 +34,7 @@
#include <linux/videodev.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv-legacy.h>
MODULE_DESCRIPTION("wm8775 driver");
MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
@@ -44,6 +45,7 @@ static unsigned short normal_i2c[] = { 0x36 >> 1, I2C_CLIENT_END };
I2C_CLIENT_INSMOD;
+
/* ----------------------------------------------------------------------- */
enum {
@@ -66,18 +68,15 @@ static int wm8775_write(struct i2c_client *client, int reg, u16 val)
return -1;
}
- for (i = 0; i < 3; i++) {
- if (i2c_smbus_write_byte_data(client, (reg << 1) |
- (val >> 8), val & 0xff) == 0) {
+ for (i = 0; i < 3; i++)
+ if (i2c_smbus_write_byte_data(client,
+ (reg << 1) | (val >> 8), val & 0xff) == 0)
return 0;
- }
- }
v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
return -1;
}
-static int wm8775_command(struct i2c_client *client, unsigned int cmd,
- void *arg)
+static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg)
{
struct wm8775_state *state = i2c_get_clientdata(client);
struct v4l2_routing *route = arg;
@@ -126,7 +125,8 @@ static int wm8775_command(struct i2c_client *client, unsigned int cmd,
break;
case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8775, 0);
+ return v4l2_chip_ident_i2c_client(client,
+ arg, V4L2_IDENT_WM8775, 0);
case VIDIOC_LOG_STATUS:
v4l_info(client, "Input: %d%s\n", state->input,
@@ -159,105 +159,67 @@ static int wm8775_command(struct i2c_client *client, unsigned int cmd,
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
-static struct i2c_driver i2c_driver;
-
-static int wm8775_attach(struct i2c_adapter *adapter, int address, int kind)
+static int wm8775_probe(struct i2c_client *client)
{
- struct i2c_client *client;
struct wm8775_state *state;
/* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return 0;
-
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == 0)
- return -ENOMEM;
-
- client->addr = address;
- client->adapter = adapter;
- client->driver = &i2c_driver;
- snprintf(client->name, sizeof(client->name) - 1, "wm8775");
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
- v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name);
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL);
- if (state == NULL) {
- kfree(client);
+ if (state == NULL)
return -ENOMEM;
- }
state->input = 2;
state->muted = 0;
i2c_set_clientdata(client, state);
- /* initialize wm8775 */
- wm8775_write(client, R23, 0x000); /* RESET */
- wm8775_write(client, R7, 0x000); /* Disable zero cross detect timeout */
- wm8775_write(client, R11, 0x021); /* Left justified, 24-bit mode */
- wm8775_write(client, R12, 0x102); /* Master mode, clock ratio 256fs */
- wm8775_write(client, R13, 0x000); /* Powered up */
- wm8775_write(client, R14, 0x1d4); /* ADC gain +2.5dB, enable zero cross */
- wm8775_write(client, R15, 0x1d4); /* ADC gain +2.5dB, enable zero cross */
- wm8775_write(client, R16, 0x1bf); /* ALC Stereo, ALC target level -1dB FS */
- /* max gain +8dB */
- wm8775_write(client, R17, 0x185); /* Enable gain control, use zero cross */
- /* detection, ALC hold time 42.6 ms */
- wm8775_write(client, R18, 0x0a2); /* ALC gain ramp up delay 34 s, */
- /* ALC gain ramp down delay 33 ms */
- wm8775_write(client, R19, 0x005); /* Enable noise gate, threshold -72dBfs */
- wm8775_write(client, R20, 0x07a); /* Transient window 4ms, lower PGA gain */
- /* limit -1dB */
- wm8775_write(client, R21, 0x102); /* LRBOTH = 1, use input 2. */
- i2c_attach_client(client);
-
+ /* Initialize wm8775 */
+
+ /* RESET */
+ wm8775_write(client, R23, 0x000);
+ /* Disable zero cross detect timeout */
+ wm8775_write(client, R7, 0x000);
+ /* Left justified, 24-bit mode */
+ wm8775_write(client, R11, 0x021);
+ /* Master mode, clock ratio 256fs */
+ wm8775_write(client, R12, 0x102);
+ /* Powered up */
+ wm8775_write(client, R13, 0x000);
+ /* ADC gain +2.5dB, enable zero cross */
+ wm8775_write(client, R14, 0x1d4);
+ /* ADC gain +2.5dB, enable zero cross */
+ wm8775_write(client, R15, 0x1d4);
+ /* ALC Stereo, ALC target level -1dB FS max gain +8dB */
+ wm8775_write(client, R16, 0x1bf);
+ /* Enable gain control, use zero cross detection,
+ ALC hold time 42.6 ms */
+ wm8775_write(client, R17, 0x185);
+ /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */
+ wm8775_write(client, R18, 0x0a2);
+ /* Enable noise gate, threshold -72dBfs */
+ wm8775_write(client, R19, 0x005);
+ /* Transient window 4ms, lower PGA gain limit -1dB */
+ wm8775_write(client, R20, 0x07a);
+ /* LRBOTH = 1, use input 2. */
+ wm8775_write(client, R21, 0x102);
return 0;
}
-static int wm8775_probe(struct i2c_adapter *adapter)
+static int wm8775_remove(struct i2c_client *client)
{
- if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, wm8775_attach);
+ kfree(i2c_get_clientdata(client));
return 0;
}
-static int wm8775_detach(struct i2c_client *client)
-{
- struct wm8775_state *state = i2c_get_clientdata(client);
- int err;
-
- err = i2c_detach_client(client);
- if (err) {
- return err;
- }
- kfree(state);
- kfree(client);
-
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-/* i2c implementation */
-static struct i2c_driver i2c_driver = {
- .driver = {
- .name = "wm8775",
- },
- .id = I2C_DRIVERID_WM8775,
- .attach_adapter = wm8775_probe,
- .detach_client = wm8775_detach,
- .command = wm8775_command,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "wm8775",
+ .driverid = I2C_DRIVERID_WM8775,
+ .command = wm8775_command,
+ .probe = wm8775_probe,
+ .remove = wm8775_remove,
};
-
-static int __init wm8775_init_module(void)
-{
- return i2c_add_driver(&i2c_driver);
-}
-
-static void __exit wm8775_cleanup_module(void)
-{
- i2c_del_driver(&i2c_driver);
-}
-
-module_init(wm8775_init_module);
-module_exit(wm8775_cleanup_module);
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index 6f1892585cbb..1fdbb46de7f3 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -749,7 +749,7 @@ static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma)
}
-static struct file_operations zr364xx_fops = {
+static const struct file_operations zr364xx_fops = {
.owner = THIS_MODULE,
.open = zr364xx_open,
.release = zr364xx_release,